Ruby:合并嵌套散列

我想合并一个嵌套的散列。

a = {:book=> [{:title=>"Hamlet", :author=>"William Shakespeare" }]} b = {:book=> [{:title=>"Pride and Prejudice", :author=>"Jane Austen" }]} 

我想合并是:

 {:book=> [{:title=>"Hamlet", :author=>"William Shakespeare"}, {:title=>"Pride and Prejudice", :author=>"Jane Austen"}]} 

什么是嵌套的方式来完成这个?

对于rails 3.0.0及更高版本, ActiveSupport的deep_merge函数完全符合您的要求。

我在这里find了一个更通用的深度合并algorithm,并且像这样使用它:

 class ::Hash def deep_merge(second) merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } self.merge(second, &merger) end end a.deep_merge(b) 

要添加到Jon M和koendc的答案中,下面的代码将处理哈希的合并,以及:nil如上所示,但是它也将联合在两个哈希(使用相同的键)中存在的任何数组:

 class ::Hash def deep_merge(second) merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 } self.merge(second.to_h, &merger) end end a.deep_merge(b) 

为了各种各样的缘故 – 只有当你想以同样的方式合并你的哈希中的所有键时才能工作 – 你可以这样做:

 a.merge(b) { |k, x, y| x + y } 

当你将一个块传递给Hash#mergek是被合并的关键,其中关键字存在于abxa[k]的值, yb[k]的值。 块的结果成为关键字k的合并散列值。

我想在你的具体情况下,nkm的答案是更好的。

有一点迟到回答你的问题,但是我写了一个相当丰富的深度合并实用程序,现在由Github上的Daniel Deleo维护: https : //github.com/danielsdeleo/deep_merge

它会完全按照你的想法合并你的数组。 从文档中的第一个示例:

所以如果你有两个这样的哈希值:

  source = {:x => [1,2,3], :y => 2} dest = {:x => [4,5,'6'], :y => [7,8,9]} dest.deep_merge!(source) Results: {:x => [1,2,3,4,5,'6'], :y => 2} 

它不会合并:y(因为int和数组不会合并) – 使用bang(!)语法会导致源代码被覆盖。使用非bang方法会在不可合并的实体find。 它将一起添加包含在x中的数组,因为它知道如何合并数组。 它处理包含任何数据结构的散列的任意深度合并。

现在在Daniel的github回购上有更多的文档..

所有的答案在我看来过于复杂。 这就是我最终想到的:

 # @param tgt [Hash] target hash that we will be **altering** # @param src [Hash] read from this source hash # @return the modified target hash # @note this one does not merge Arrays def self.deep_merge!(tgt_hash, src_hash) tgt_hash.merge!(src_hash) { |key, oldval, newval| if oldval.kind_of?(Hash) && newval.kind_of?(Hash) deep_merge!(oldval, newval) else newval end } end 

PS作为公众使用,WTFPL或任何许可证

 a[:book] = a[:book] + b[:book] 

要么

 a[:book] << b[:book].first 

我认为Jon M的答案是最好的,但是当你用一个零/未定义值的散列进行合并时,它会失败。 此更新解决了这个问题:

 class ::Hash def deep_merge(second) merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : [:undefined, nil, :nil].include?(v2) ? v1 : v2 } self.merge(second, &merger) end end a.deep_merge(b)