Ruby:嵌套哈希在与另一个嵌套哈希合并时会丢失键和值



我有一个散列-

h1 = {"a"=>{"x"=>"1","y"=>"2"}, "b"=>"z"}

我有另一个散列h2,它基本上为h1中的一些密钥更新了值

h2 = {"a"=>{"x"=>"3"}, "b"=>"q"}

当我做时

h1.merge(h2)

我期待它回来-

{"a"=>{"x"=>"3","y"=>"2"}, "b"=>"q"}

但它实际上给了我——{"a"=>{"x"=>"3"}, "b"=>"q"}

我需要做什么才能获得{"a"=>{"x"=>"3","y"=>"2"}, "b"=>"q"}

我不是在使用铁轨。ruby版本是2.6.0

Hash#merge的工作方式是一致的:如果键在两者中都存在,那么在所有情况下,合并后的散列的键值都会替换第一个。您需要一个递归合并。

Rails中提供了一个deep_merge。但是,如果您不使用Rails,或者它不适合您的需求,您可以很容易地推出自己的Rails。

Hash#merge确实支持一个有帮助的块:

h1.merge(h2) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
old.merge(new) : new }

如果你只有一层深度的嵌入散列,这将起作用。如果你有任意深度的嵌套散列,你可以用deep_merge:对Hash进行猴子补丁

class Hash
def deep_merge(h)
self.merge(h) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
old.deep_merge(new) : new }
end
end

或者类似的东西…:(如果在这两种情况下都有散列,这将递归合并,否则,它将照常替换。你可以根据自己的口味来修改它。

在你的案例中尝试一下:

2.6.1 :008 > class Hash
2.6.1 :009?>   def deep_merge(h)
2.6.1 :010?>     self.merge(h) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
2.6.1 :011 >                                   old.deep_merge(new) : new }
2.6.1 :012?>   end
2.6.1 :013?> end
=> :deep_merge
2.6.1 :014 > h1 = {"a"=>{"x"=>"1","y"=>"2"}, "b"=>"z"}
=> {"a"=>{"x"=>"1", "y"=>"2"}, "b"=>"z"}
2.6.1 :015 > h2 = {"a"=>{"x"=>"3"}, "b"=>"q"}
=> {"a"=>{"x"=>"3"}, "b"=>"q"}
2.6.1 :016 > h1.deep_merge(h2)
=> {"a"=>{"x"=>"3", "y"=>"2"}, "b"=>"q"}
2.6.1 :017 >

最新更新