For this array:
items = [[60, 3], [60, 3], [276, 2], [276, 2], [48, 2], [207, 2], [46, 2],
[60, 2], [280, 2], [207, 1], [48, 1], [112, 1], [60, 1], [207, 1], [112, 1],
[276, 1], [48, 1], [276, 1], [48, 1], [276, 1], [276, 1], [278, 1], [46, 1],
[48, 1], [279, 1], [207, 1]]
I want to combine common numbers in the first positions of each sub-array, and add the numbers in the second positions together.
For instance, you'll see the first four sub-arrays here are: [60, 3], [60, 3], [276, 2], [276, 2]
This would become: [60,6], [276,4]
and so on.
Try this
items.
group_by {|i| i[0]}.
map{|key, value| [key,value.inject(0){|sum, x| sum + x[1]}]}
Firstly, use group_by to create a hash whose keys is the first element of each array. So we have
{
60=>[[60, 3], [60, 3], [60, 2], [60, 1]],
276=>[[276, 2], [276, 2], [276, 1], [276, 1], [276, 1], [276, 1]],
48=>[[48, 2], [48, 1], [48, 1], [48, 1], [48, 1]],
207=>[[207, 2], [207, 1], [207, 1], [207, 1]],
46=>[[46, 2], [46, 1]],
280=>[[280, 2]],
112=>[[112, 1], [112, 1]],
278=>[[278, 1]],
279=>[[279, 1]]
}
Then to create the desired result, using map method to loop the hash. To calculate total value for each key, using inject method to sum all second value of each array
[[60, 3], [60, 3], [60, 2], [60, 1]].inject(0) {|sum, x| sum + x[1]} #value is 9
You could use Enumerable#each_with_object
Iterates the given block for each element with an arbitrary object given, and returns the initially given object.
items.each_with_object(Hash.new(0)) {|a, h| h[a[0]] += a[1]}.to_a
# => [[60, 9], [276, 8], [48, 6], [207, 5], [46, 3], [280, 2],
# [112, 2], [278, 1], [279, 1]]
From Stefen's comment
The Array passed to the block can be decomposed like this
items.each_with_object(Hash.new(0)) {|(k,v), h| h[k] += v}.to_a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With