I have an array like
[
[358, 202102, 5],
[358, 202112, 5],
[358, 202102, 10],
[311, 202103, 5],
[311, 202101, 1],
[311, 202101, 1],
[311, 202115, 8],
[311, 202101, 1],
[311, 202101, 1]
]
I need a hash like this:
{
358 => {
202102 => 15,
202112 => 5
},
311 => {
202103 => 5,
202101 => 4,
202115 => 8
}
}
The order does not matter. Only the third values from each row of the array must be added up.
You can solve this using Enumerable#each_with_object
as follows
a = [
[358, 202102, 5],
[358, 202112, 5],
[358, 202102, 10],
[311, 202103, 5],
[311, 202101, 1],
[311, 202101, 1],
[311, 202115, 8],
[311, 202101, 1],
[311, 202101, 1]
]
a.each_with_object(Hash.new {|h,k| h[k] = Hash.new(0)}) do |(k1,k2,val), obj|
obj[k1][k2] += val
end
# => {358=>{202102=>15, 202112=>5}, 311=>{202103=>5, 202101=>4, 202115=>8}}
What this does is iterates over the Array (a
) with an accumulator Object (obj
).
This object is a Hash
with a default proc where every time a new key is added its value is assigned as a new Hash with a default value of 0.
In the block arguments we deconstruct the Array into 3 arguments (k1,k2,val)
so on first pass it would look something like this:
k1 = 358
k2 = 202102
val = 5
obj[k1]
#=> {358=> {}}
obj[k1][k2]
#=> {358 => {202102 => 0 }}
obj[k1][k2] += val
obj
#=> {358 => {202102 => 5 }}