Reputation: 15141
I have two hashes like these.
a = {foo: {first: 1}, bar: {first: 2}}
b = {foo: {second: 3}, bar: {second: 4}}
And I expected as a result of a.merge b
like this.
{foo: {first: 1, second: 3}, bar: {first: 2, second: 4}}
But a.merge b
returns {:foo=>{:second=>3}, :bar=>{:second=>4}}
.
How can I merge two hashes without losing values?
Upvotes: 2
Views: 1259
Reputation: 110665
You should use the form of Hash#merge that takes a block that determines the value associated with every key that is present in both hashes.
Code
def merge_em(a,b)
a.merge(b) { |k,va,vb| va.merge(vb) }
end
Example
a = {foo: {first: 1}, bar: {first: 2}}
b = {foo: {second: 3}, bar: {second: 4}}
merge_em(a,b)
#=> {:foo=>{:first=>1, :second=>3}, :bar=>{:first=>2, :second=>4}}
Explanation
Here the block variables are as follows:
`k` : the key
`va`: the value for key `k` in the merged hash (`a`)
`vb`: the value for key `k` in the hash being merged (`b`)
We want the value for key k
to be simply:
va.merge(vb)
Upvotes: 0
Reputation: 9752
if the format of your hash would always look like you specified the below would work:
a = {foo: {first: 1}, bar: {first: 2}}
b = {foo: {second: 3}, bar: {second: 4}}
a.each_with_object(b) { |(k,v),x| x[k].merge!(v) }
# => {:foo=>{:second=>3, :first=>1}, :bar=>{:second=>4, :first=>2}}
Otherwise use ActiveSupport
's deep_merge!
Upvotes: 5