O'hare
O'hare

Reputation: 13

Merge hashes with same key value pair inside array

How to merge hashes within array with same key, value pair? I have following array,

arry = [{"id" => 1, "val1" => 123},
        {"id" => 2, "val1" => 234},
        {"id" => 1, "val2" => 321},
        {"id" => 1, "val3" => 445},
        {"id" => 3, "val3" => 334}]

Want to get

arry = [{"id" => 1, "val1" => 123, "val2" => 321, "val3" => 445},
        {"id" => 2, "val1" => 234},
        {"id" => 3, "val3" => 334}]

is there ruby way to do it? Tried few ways but no success so far.

Upvotes: 0

Views: 425

Answers (3)

MonR
MonR

Reputation: 121

This should work too,

arry.group_by { |a| a['id'] }.map{|_, ar| ar.reduce(:merge)}

This would return,

[{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445}, {"id"=>2, "val1"=>234}, {"id"=>3, "val3"=>334}]

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110645

arry.each_with_object({}) { |g,h| h.update(g["id"]=>g) { |_,o,n| o.merge(n) } }.values
  #=> [{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
  #    {"id"=>2, "val1"=>234},
  #    {"id"=>3, "val3"=>334}] 

Note the receiver of values is:

{1=>{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
 2=>{"id"=>2, "val1"=>234},
 3=>{"id"=>3, "val3"=>334}} 

This uses the form of Hash#update (aka merge) that employs the block { |_,o,n| o.merge(n) } to determine the values of keys that are present in both hashes being merged. See the doc for descriptions of the three block variables. (I've used an underscore for the first, the common key, to signify that it is not used in the block calculation.)

Upvotes: 1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 120990

The arry you have posted is not a valid ruby array in the first place (I fixed it in my edit.)

arry.
  group_by { |h| h["id"] }.
  values.
  map { |a| a.reduce(&:merge) }
#⇒ [{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
#   {"id"=>2, "val1"=>234}, {"id"=>3, "val3"=>334}]

If your input might have same keys within the same "id" (like {"id" => 1, "val1" => 123}, {"id" => 1, "val1" => 456},) you are to decide how to merge them. In any case, Hash#merge with a block would be your friend there.

Upvotes: 7

Related Questions