8

How to merge array of hash based on the same keys in ruby?

example :

a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]

How to get result like this?

a = [{:a=>[1, 10]},{:b=>8},{:c=>[7, 2]}]
1
  • 1
    Me guess that keeping polymorphic values (sometimes 7, sometimes [ 7, 7 ]) is not such a good practice. Why not just keep this in a single hash of arrays rather than an array of hashes whose values are sometimes arrays? Commented Mar 11, 2014 at 9:11

4 Answers 4

11

Try

a.flat_map(&:entries)
  .group_by(&:first)
  .map{|k,v| Hash[k, v.map(&:last)]}
Sign up to request clarification or add additional context in comments.

Comments

6

Another alternative:

a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]

p a.each_with_object({}) { |h, o| h.each { |k,v| (o[k] ||= []) << v } }
# => {:a=>[1, 10], :b=>[8], :c=>[7, 2]}

It also works when the Hashes have multiple key/value combinations, e.g:

b = [{:a=>1, :b=>5, :x=>10},{:a=>10, :y=>2},{:b=>8},{:c=>7},{:c=>2}]
p b.each_with_object({}) { |h, o| h.each { |k,v| (o[k] ||= []) << v } }
# => {:a=>[1, 10], :b=>[5, 8], :x=>[10], :y=>[2], :c=>[7, 2]}

1 Comment

If you don't mind... Can I use this (hsh[k] ||= []) << v in my code? I was looking for this. But couldn't remember anyway.
3

Minor addition to answer by Arie Shaw to match required answer:

a.flat_map(&:entries)
  .group_by(&:first)
  .map{|k,v| Hash[k, v.size.eql?(1) ? v.last.last : v.map(&:last) ]}
#=> [{:a=>[1, 10]}, {:b=>8}, {:c=>[7, 2]}]

Comments

2

I'd do :

a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
merged_hash = a.each_with_object({})  do |item,hsh|
  k,v = item.shift
  hsh[k] = hsh.has_key?(k) ? [ *Array( v ), hsh[k] ] : v
end

merged_hash.map { |k,v| { k => v } }
# => [{:a=>[10, 1]}, {:b=>8}, {:c=>[2, 7]}]

update

A better taste :

a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
merged_hash = a.each_with_object({})  do |item,hsh|
  k,v = item.shift
 (hsh[k] ||= []) << v
end

merged_hash.map { |k,v| { k => v } }
# => [{:a=>[10, 1]}, {:b=>8}, {:c=>[2, 7]}]

3 Comments

Arup, your solution is much cleaner than the other two (I don't like flat_map), but you made one mistake. If you try a = [{a: 1}, {a: 2}, {a: 3}], you will get a nested array. I'm correcting it.
@BorisStitnicky Can I write now hsh[k] = hsh.has_key?(k) ? [*hsh[k]] << v : v ?
@BorisStitnicky I didn't check that although. :(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.