Related
Let's say I want to get a certain number of even groups based on a collection of records with varying count. How is this possible?
I'm looking for a method like objects.in_x_even_groups(4)
Group your objects by their index modulo the number of groups.
objects.group_by.with_index { |_, i| i % num_groups }.values
Example:
objects = %w{a b c d e f g h i j k}
objects.group_by.with_index { |_, i| i % 3 }.values
# [["a", "d", "g", "j"], ["b", "e", "h", "k"], ["c", "f", "i"]]
This won't pad undersized groups with nil and it also will interleave your objects. So this won't work if you need consecutive objects to be in the same group.
You are probably looking for the in_groups method. From the docs:
in_groups(number, fill_with = nil)
Splits or iterates over the array in number of groups, padding any remaining slots with fill_with unless it is false.
%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
["1", "2", "3", "4"]
["5", "6", "7", nil]
["8", "9", "10", nil]
I assume:
the elements are to be kept in order;
l-s is to be minimized, where l is the size of the largest group and s is the size of the smallest group; and
group sizes are non-increasing.
l-s will be at most 1.
def group_em(arr, ngroups)
n_per_group, left_over = arr.size.divmod(ngroups)
cum_off = 0
ngroups.times.map do |i|
n = n_per_group + (i < left_over ? 1 : 0)
a = arr[cum_off, n]
cum_off += n
a
end
end
arr = [1, 2, 3, 4, 5, 6, 7]
(1..7).each { |m| puts "ngroups=#{m}: #{group_em(arr, m)}" }
ngroups=1: [[1, 2, 3, 4, 5, 6, 7]]
ngroups=2: [[1, 2, 3, 4], [5, 6, 7]]
ngroups=3: [[1, 2, 3], [4, 5], [6, 7]]
ngroups=4: [[1, 2], [3, 4], [5, 6], [7]]
ngroups=5: [[1, 2], [3, 4], [5], [6], [7]]
ngroups=6: [[1, 2], [3], [4], [5], [6], [7]]
ngroups=7: [[1], [2], [3], [4], [5], [6], [7]]
You're looking for in_groups_of:
https://apidock.com/rails/Array/in_groups_of
array = %w(1 2 3 4 5 6 7 8 9 10)
array.in_groups_of(3) {|group| p group}
=>
["1", "2", "3"]
["4", "5", "6"]
["7", "8", "9"]
["10", nil, nil]
I have 2 arrays that I've zipped together and now I'm trying to swipe values at even positions.
So this is what I've tried:
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
c = a.zip(b)
# Now c is equal to: [[1, 111], [2, 222],[3, 333],[4, 444]]
c.map.with_index do |item, index|
a = item[0]
b = item[1]
if index%2 == 0
a, b = b, a
end
end
What I would like to have:
c = [[1, 111], [222,2], [3, 333],[444, 4]]
But it's still not working, is there a better solution ? Or how could I fix mine to make it work ?
EDIT:
I've realized that I could probably just use the ".reverse" method to swap the element. But I still can't manage to make it work.
Perhaps try:
c.map.with_index do |item, index|
index%2 != 0 ? item.reverse : item
end
=> [[1, 111], [222, 2], [3, 333], [444, 4]]
I would probably go with
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
a.zip(b).each_with_index do |item, idx|
item.reverse! if idx.odd?
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
zip as you did and reverse! just the items where the index is odd.
Other options include:
a.map.with_index(1) do |item,idx|
[item].insert(idx % 2, b[idx -1])
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we use with_index starting with 1 and then use the modulo method to determine if the item in b should be placed at index 0 or index 1.
Or
a.zip(b).tap {|c| c.each_slice(2) {|_,b| b.reverse!}}
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we zip a and b as your example did then we take the sub Arrays in groups of 2 and reverse the second Array using reverse! which will modify the Array in place.
I have an array of strings. I'm wanting to change the name of these duplicate strings to append a numerical value to make them unique like so...
Original Array
a, a, A, b, c, D, d
Corrected Array
a, a1, A2, b, c, D, d1
I've gotten close to this with the following code; however, if the strings are a different case structure then they aren't currently considered duplicates with this code snippet. I would like them to be considered duplicates, but yet not change their case in the results array.
duplicate_counter = 1
duplicates = Array.new
duplicates = file_columns.select{ |e| file_columns.count(e) > 1 } # get duplicate column names
duplicates.each{ |x| file_columns.delete(x) }
duplicates.sort!
duplicates.each_with_index do |d, i|
if i > 0
if d == duplicates[i-1]
d = d.strip + duplicate_count.to_s
duplicate_count += 1
else
duplicate_count = 1
end
end
# Add back the column names, but with the appended numerical counts to make them unique
file_columns.push(d)
end
You are over thinking it considerably. I'm sure there are better ways to do this as well, but it gets the job done.
a = ['a', 'a', 'A', 'b', 'c', 'D', 'd']
letters = Hash.new(-1)
a.map do |letter|
l = letter.downcase
letters[l] += 1
if (letters[l] > 0)
"#{letter}#{letters[l]}"
else
"#{letter}"
end
end
Here's a way to do it if letters independent of case are not necessarily grouped. For example, it will convert this array:
arr = %w{ a D a A b c D a d }
#=> ["a", "D", "a", "A", "b", "c", "D", "a", "d"]
to:
["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
Code
def convert(arr)
arr.each_with_index
.group_by { |c,_| c.downcase }
.values
.flat_map { |c|
c.map
.with_index { |(f,l),i| [i > 0 ? f<<i.to_s : f, l] } }
.sort_by(&:last)
.map(&:first)
end
Example
For arr above:
convert(arr)
#=> ["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
Explanation
Dear reader, if you are new to Ruby, this may look impossibly complex. If you break it down into steps, however, it's not that bad. After you gain experience and become familiar with commonly-used methods, it will come quite naturally. Here I've used the following methods, chained together so that the return value of each becomes the receiver of the next:
Enumerable#each_with_index
Enumerable#group_by
Hash#values
Enumerable#flat_map
Enumerable#sort_by
Enumerable#first
Here's what's happening.
enum = arr.each_with_index
#=> #<Enumerator: ["a", "D", "a", "A", "b", "c",
# "D", "a", "d"]:each_with_index>
h = enum.group_by { |c,_| c.downcase }
#=> {"a"=>[["a", 0], ["a", 2], ["A", 3], ["a", 7]],
# "d"=>[["D", 1], ["D", 6], ["d", 8]],
# "b"=>[["b", 4]],
# "c"=>[["c", 5]]}
a = h.values
#=> [[["a", 0], ["a", 2], ["A", 3], ["a", 7]],
# [["D", 1], ["D", 6], ["d", 8]],
# [["b", 4]],
# [["c", 5]]]
b = a.flat_map { |c| c.map.with_index { |(f,l),i| [i > 0 ? f<<i.to_s : f, l] } }
#=> [["a", 0], ["a1", 2], ["A2", 3], ["a3", 7], ["D", 1],
# ["D1", 6], ["d2", 8], ["b", 4], ["c", 5]]
c = b.sort_by(&:last)
#=> [["a", 0], ["D", 1], ["a1", 2], ["A2", 3], ["b", 4],
# ["c", 5], ["D1", 6], ["a3", 7], ["d2", 8]]
c.map(&:first)
#=> ["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
I got the mistake when I want to add doubled values to an array:
arr = [1,2,3]
def my_mistake(arr)
result = Array.new
arr.map { |element| result << element * 2 }
end
#=> [[2, 4, 6], [2, 4, 6], [2, 4, 6]]
def solution(arr)
arr.map { |element| element * 2 }
end
#=> [2,4,6]
However, come back to my mistake and the definition of map method in Ruby.
Invokes the given block once for each element of self. Creates a new array containing the values returned by the block.
I think my_mistake method has to return [[2], [2, 4], [2, 4, 6]] but it doesn't.
Everyone can explain this case for me ?
The resulting array will contain three occurrences of the same reference to the same array, as result is the result of the expression result << element * 2. So the result of the map is (kind of) [result, result, result]. These all point to the same content, which is the content of result at the end of the process ([2, 4, 6]).
What you expected would be achieved if you clone the array at each point, so that every resulting element would point to a different array, and each addition would not affect the previously computed arrays:
arr.map { |element| (result << element * 2).clone }
=> [[2], [2, 4], [2, 4, 6]]
.map returns the last evaluated expression, so no need for the result << part there. Here's something that worked for me:
def my_mistake(arr)
result = [] # '= []' is same like '= Array.new', look-up "literal constructors in Ruby"
new_arr = [] # same like new_arr = Array.new
until arr.empty?
new_arr << arr.shift # we add each element of arr, one by one, starting from the beginning
output << new_arr.map { |e| e * 2} # we calculate *2 for each element
end
return result
end
p my_mistake(arr) #=> [[2], [2, 4], [2, 4, 6]]
If you're nto sure how this works, try putting "p output" after the 6th line:
def my_mistake(arr)
output = []
new_arr = []
until arr.empty?
new_arr << arr.shift
output << new_arr.map { |e| e * 2}
p output
end
return output
end
my_mistake(arr)
The program will print:
[[2]]
[[2], [2, 4]]
[[2], [2, 4], [2, 4, 6]]
I have an array which contains numbers and alphabets something like:
newArray = ["1 a", "1 b" ,"2 c", "2 a"]
I would like to sort them in a way that the output is expected as follows:
newArray = ["2 a", "2 c" ,"1 a", "1 b"]
What I want to do is sort the numbers in descending order and if the numbers are same, then sort alphabetically
Can I implement a comparison function in sort_by or is there a way to do that using ruby sort
First you should use a better representation of your input. You can parse your existing array for example like this:
arr = newArray.map { |s| x,y = s.split; [x.to_i, y] }
# => [[1, "a"], [1, "b"], [2, "c"], [2, "a"]]
Then we can sort as we wish using sort_by:
arr.sort_by { |x,y| [-x, y] }
# => [[2, "a"], [2, "c"], [1, "a"], [1, "b"]]
Similar to #NiklasB. 's answer above (copied his sort_by)
arr.map(&:split).sort_by { |x,y| [-x.to_i, y] }
=> [["2", "a"], ["2", "c"], ["1", "a"], ["1", "b"]]
In a less elegant way, you can do that
arr.sort! do |p1, p2|
num1, str1 = p1.split(' ')
num2, str2 = p2.split(' ')
if (num1 != num2)
p2 <=> p1
else
p1 <=> p2
end
end
$stdout.puts arr