简体   繁体   中英

How do I merge two hashes into a hash of arrays?

http://codepad.org/WmYzQLiS

I'd like to merge these two arrays:

a = { :a => 'a', :b => 'b', :d => 'd' }
b = { :a => '1', :b => 'b', :c => 'c' }

Into a hash that looks like this:

{:a => ["a", "1"], :b => ["b", "b"], :c => [nil, "c"], :d => ["d", nil] }

This obviously doesn't work:

p a.merge(b) { |k, v1, v2| [v1, v2] }    # {:c=>"c", :a=>["a", "1"], :b=>["b", "b"], :d=>"d"}

It's because Hash#merge call the block only for the duplicate keys.


a = { :a => 'a', :b => 'b', :d => 'd' }
b = { :a => '1', :b => 'b', :c => 'c' }

Hash[(a.keys|b.keys).map { |key|
  [key, [a.fetch(key, nil), b.fetch(key, nil)]]
}]
# => {:a=>["a", "1"], :b=>["b", "b"], :d=>["d", nil], :c=>[nil, "c"]}

# In Ruby 2.1+
(a.keys|b.keys).map { |key|
  [key, [a.fetch(key, nil), b.fetch(key, nil)]]
}.to_h
(a.keys | b.keys).each_with_object({}) { |k,h| h[k] = [a[k], b[k]] }
  #=> {:a=>["a", "1"], :b=>["b", "b"], :d=>["d", nil], :c=>[nil, "c"]}

This is easy to generalize for an arbitrary number of hashes.

arr = [{ :a => 'a', :b => 'b',            :d => 'd' },
       { :a => '1', :b => 'b', :c => 'c'            },
       {            :b => '0', :c => 'b', :d => 'c' }]

arr.reduce([]) { |ar,h| ar | h.keys }
   .each_with_object({}) { |k,h| h[k] = arr.map { |g| g[k] } }
  #=> {:a=>["a", "1", nil], :b=>["b", "b", "0"],
  #    :d=>["d", nil, "c"], :c=>[nil, "c", "b"]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM