From: Florian Gross Date: 2005-03-15T22:57:38+09:00 Subject: Re: YAML obj merging gga wrote: > The problem is that I want hashes that contain other hashes to also > follow the same rule. > > That is: > irb> a = { 'A' => { 'A1' => 'a' } } > irb> b = { 'A' => { 'B1' => 'b' } } > irb> b.merge(a) > {"A"=>{"A1"=>"a"}} > > When what I really want is this to be done recursively, so that: > irb> a = { 'A' => { 'A1' => 'a' } } > irb> b = { 'A' => { 'B1' => 'b' } } > irb> b.mix(a) # ficticious method > {"A"=>{"A1"=>"a", "B1" => 'b' }} > > I guess I probably need to add my own method to do this. I'm coming a bit late to this discussion (I've only spotted it after I've read through this weeks' Ruby Weekly News), but here's my version: class Hash # Similar to Hash#merge, but this version works recursively so that # nested Hashs will be merged as well. You can specify a collision # block which will be invoked for conflicting items. (It takes the # same argument list as the collision block supplied to Hash#merge: # |hash, item1, item2|) # # Usage example: # a = { "A" => { "A1" => "a" } } # b = { "A" => { "B1" => "b" } } # b.mix(a) # => {"A" => {"A1" => "a", "B1" => "b" }} def mix(other, &collision_block) merge_block = lambda do |hash, item1, item2| if item1.is_a?(Hash) and item2.is_a?(Hash) then item1.merge(item2, &merge_block) elsif collision_block then collision_block.call(hash, item1, item2) else item2 end end self.merge(other, &merge_block) end end So basically the block that Hash#merge can take is insanely useful. I've also used for just detecting Hash collisions in the past as well.