Another way uses the form of Hash#update (aka merge
) that uses a block to determine the values of keys that are present in both hashes being merged:
a.each_with_object({}) do |(name,id),h|
h.update(name=>[id]) { |_,oid,nid| oid.concat(nid) }
end
#=> {"user_3"=>[765], "user_1"=>[2, 1, 3], "user_2"=>[124, 223, 334]}
Let's see what's happening here:
e = a.each_with_object({})
#=> #<Enumerator: [["user_3", 765], ["user_1", 2], ["user_1", 1],
# ["user_2", 124], ["user_1", 3], ["user_2", 223],
# ["user_2", 334]]:each_with_object({})>
The first element of e
is passed to each_with_object
's block and the block calculation is performed:
(name,id),h = e.next
#=> [["user_3", 765], {}]
name #=> "user_3"
id #=> 765
h #=> {}
h.update(name=>[id])
#=> {}.update("user_3"=>[765])
#=> {"user_3"=>[765]}
update
's block is not used here because the key "user_3"
is not present in h
. The next value of the enumerator is now passed to the block:
(name,id),h = e.next
#=> [["user_1", 2], {"user_3"=>[765]}]
h.update(name=>[id])
#=> {"user_3"=>[765]}.update("user_1"=>[2])
#=> {"user_3"=>[765], "user_1"=>[2]}
Again, the block is not used because h
does not have a key "user_1"
. The third element of e
is passed to the block:
(name,id),h = e.next
#=> [["user_1", 1], {"user_3"=>[765], "user_1"=>[2]}]
h.update(name=>[id])
#=> {"user_3"=>[765], "user_1"=>[2]}.update("user_1"=>[1])
This time, both hashes being merged have the key "user_1"
, so the block is called up to determine the value of that key:
{ |key,oid,nid| oid.concat(nid) }
#=> { |"user_1",[2],[1]| [2].concat([1]) }
#=> [2,1]
so now:
h #=> {"user_3"=>[765], "user_1"=>[2, 1]}
I have drawn attention to the fact that the argument key
was not used in this block calculation by replacing that variable with an underscore (which is indeed a variable). Some Rubiests might write _key
instead.
The remaining calculations are similar.