Recursive merge for hash

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

On 15/02/2008, goodieboy [email protected] 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

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.

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

Jimmy K. 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

Stefan R. wrote:
Jimmy K. 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.