Forum: Ruby recursive merge for hash

444c7d8579d3389a76c8497f5cb66c13?d=identicon&s=25 M.W. Mitchell (goodieboy)
on 2008-02-15 16:37
(Received via mailing list)
Hi,

Does anyone know of a good recursive merge method for Hashes? I'd like
to be able to do something like:

new_h = first.merge_r(second).merge_r(third)

... everything would be a copy, and hashes applied later in the
sequence override the previous finally, returning a completely new
hash... is there something out there like this?

Thanks,
Matt
05be5d6610e2c3f1780aa0e39e902e93?d=identicon&s=25 Farrel Lifson (Guest)
on 2008-02-15 16:49
(Received via mailing list)
On 15/02/2008, goodieboy <goodieBoy@gmail.com> wrote:
> Hi,
>
>  Does anyone know of a good recursive merge method for Hashes? I'd like
>  to be able to do something like:
>
>  new_h = first.merge_r(second).merge_r(third)
>
>  ... everything would be a copy, and hashes applied later in the
>  sequence override the previous finally, returning a completely new
>  hash... is there something out there like this?

new_h = first.merge(second).merge(third)

Farrel
444c7d8579d3389a76c8497f5cb66c13?d=identicon&s=25 M.W. Mitchell (goodieboy)
on 2008-02-15 16:57
(Received via mailing list)
Thanks. But the only problem with that is, the overriding hash will
completely destroy *all* of the values in the original. So if I want
to just override one value, I get only one value. In this example,
"Sam" is lost:

first = {
  :data=>{
    :name=>{
      :first=>'Sam',
      :middle=>'I',
      :last=>'am'
    }
  }
}

second={
  :data=>{
    :name=>{
      :middle=>'you',
      :last=>'are'
    }
  }
}
all_new = first.merge(second)

puts all_new.inspect
E7fe24cfaaf8af56ae28f63c81363172?d=identicon&s=25 Jimmy Kofler (koflerjim)
on 2008-02-16 14:51
> M.W. Mitchell wrote:
> Thanks. But the only problem with that is, the overriding hash will
> completely destroy *all* of the values in the original. So if I want
> to just override one value, I get only one value. In this example,
> "Sam" is lost:
>
> first = {
>   :data=>{
>     :name=>{
>       :first=>'Sam',
>       :middle=>'I',
>       :last=>'am'
>     }
>   }
> }
>
> second={
>   :data=>{
>     :name=>{
>       :middle=>'you',
>       :last=>'are'
>     }
>   }
> }
> all_new = first.merge(second)
>
> puts all_new.inspect


How about Hash#deep_merge, http://snippets.dzone.com/posts/show/4706 ?

Cheers,
j.k.
Cf7cd97cdc8ed7d4ae92965b24f0dfad?d=identicon&s=25 Stefan Rusterholz (apeiros)
on 2008-02-19 21:51
Jimmy Kofler wrote:
>> M.W. Mitchell wrote:
>> Thanks. But the only problem with that is, the overriding hash will
>> completely destroy *all* of the values in the original. So if I want
>> to just override one value, I get only one value. In this example,
>> "Sam" is lost:
>>
>> first = {
>>   :data=>{
>>     :name=>{
>>       :first=>'Sam',
>>       :middle=>'I',
>>       :last=>'am'
>>     }
>>   }
>> }
>>
>> second={
>>   :data=>{
>>     :name=>{
>>       :middle=>'you',
>>       :last=>'are'
>>     }
>>   }
>> }
>> all_new = first.merge(second)
>>
>> puts all_new.inspect
>
>
> How about Hash#deep_merge, http://snippets.dzone.com/posts/show/4706 ?
>
> Cheers,
> j.k.

The question came up on IRC recently and I remembered this post. But I
wonder why the snippet does it in such a complicated way. Alternative:
merger = proc { |key,v1,v2| Hash === v1 && Hash === v2 ? v1.merge(v2,
&merger) : v2 }
first.merge(second, &merger)

Regards
Stefan
E7fe24cfaaf8af56ae28f63c81363172?d=identicon&s=25 Jimmy Kofler (koflerjim)
on 2008-02-21 16:52
> Stefan Rusterholz wrote:
> Jimmy Kofler wrote:
>>> M.W. Mitchell wrote:
>>> Thanks. But the only problem with that is, the overriding hash will
>>> completely destroy *all* of the values in the original. So if I want
>>> to just override one value, I get only one value. In this example,
>>> "Sam" is lost:
>>>
>>> first = {
>>>   :data=>{
>>>     :name=>{
>>>       :first=>'Sam',
>>>       :middle=>'I',
>>>       :last=>'am'
>>>     }
>>>   }
>>> }
>>>
>>> second={
>>>   :data=>{
>>>     :name=>{
>>>       :middle=>'you',
>>>       :last=>'are'
>>>     }
>>>   }
>>> }
>>> all_new = first.merge(second)
>>>
>>> puts all_new.inspect
>>
>>
>> How about Hash#deep_merge, http://snippets.dzone.com/posts/show/4706 ?
>>
>> Cheers,
>> j.k.
>
> The question came up on IRC recently and I remembered this post. But I
> wonder why the snippet does it in such a complicated way. Alternative:
> merger = proc { |key,v1,v2| Hash === v1 && Hash === v2 ? v1.merge(v2,
> &merger) : v2 }
> first.merge(second, &merger)
>
> Regards
> Stefan

Well, maybe it's a bit easier to modify or extend the verbose version.

For example:

class Hash
   def keep_merge(hash)
      target = dup
      hash.keys.each do |key|
         if hash[key].is_a? Hash and self[key].is_a? Hash
            target[key] = target[key].keep_merge(hash[key])
            next
         end
         #target[key] = hash[key]
         target.update(hash) { |key, *values| values.flatten.uniq }
      end
      target
   end
end


first = {
  :data=>{
    :name=>{
      :first=>'Sam',
      :middle=>'I',
      :last=>'am'
    }
  }
}

second={
  :data=>{
    :name=>{
      :middle=>'you',
      :last=>'are'
    }
  }
}

p first.keep_merge(second)
#=>  {:data=>{:name=>{:first=>"Sam", :middle=>["I", "you"],
:last=>["am", "are"]}}}

Anyway, a nice refactoring example!


Cheers,
j. k.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.