在Ruby中合并多维哈希

Whi*_*ant 6 ruby ruby-on-rails ruby-on-rails-3

我有两个哈希,其结构与此类似:

hash_a = { :a => { :b => { :c => "d" } } }
hash_b = { :a => { :b => { :x => "y" } } }
Run Code Online (Sandbox Code Playgroud)

我想将这些合并在一起以产生以下哈希:

{ :a => { :b => { :c => "d", :x => "y" } } }
Run Code Online (Sandbox Code Playgroud)

merge函数将在第一个哈希值中替换:a的值,其值为:第二个哈希值中的a.所以,我编写了自己的递归合并函数,如下所示:

def recursive_merge( merge_from, merge_to )
    merged_hash = merge_to
    first_key = merge_from.keys[0]
    if merge_to.has_key?(first_key)
        merged_hash[first_key] = recursive_merge( merge_from[first_key], merge_to[first_key] )
    else
        merged_hash[first_key] = merge_from[first_key]
    end
    merged_hash
end
Run Code Online (Sandbox Code Playgroud)

但我得到一个运行时错误:can't add a new key into hash during iteration.在Ruby中合并这些哈希的最佳方法是什么?

Phr*_*ogz 10

Ruby的现有版本Hash#merge允许使用块形式来解决重复项,这使得它非常简单.我添加了将树的"叶子"中的多个冲突值合并为数组的功能; 你可以选择选择一个或另一个.

hash_a = { :a => { :b => { :c => "d", :z => 'foo' } } }
hash_b = { :a => { :b => { :x => "y", :z => 'bar' } } }

def recurse_merge(a,b)
  a.merge(b) do |_,x,y|
    (x.is_a?(Hash) && y.is_a?(Hash)) ? recurse_merge(x,y) : [*x,*y]
  end
end

p recurse_merge( hash_a, hash_b )
#=> {:a=>{:b=>{:c=>"d", :z=>["foo", "bar"], :x=>"y"}}}
Run Code Online (Sandbox Code Playgroud)

或者,作为一个干净的猴子补丁:

class Hash
  def merge_recursive(o)
    merge(o) do |_,x,y|
      if x.respond_to?(:merge_recursive) && y.is_a?(Hash)
        x.merge_recursive(y)
      else
        [*x,*y]
      end
    end
  end
end

p hash_a.merge_recursive hash_b
#=> {:a=>{:b=>{:c=>"d", :z=>["foo", "bar"], :x=>"y"}}}
Run Code Online (Sandbox Code Playgroud)

  • "干净的猴子补丁"?这不是矛盾吗?:-) (3认同)

Dou*_*las 8

你可以在一行中完成:

merged_hash = hash_a.merge(hash_b){|k,hha,hhb| hha.merge(hhb){|l,hhha,hhhb| hhha.merge(hhhb)}}
Run Code Online (Sandbox Code Playgroud)

如果你想merge将结果imediatly变成hash_a,只需用方法替换方法mergemerge!

如果您使用rails 3或rails 4框架,则更容易:

merged_hash = hash_a.deep_merge(hash_b)
Run Code Online (Sandbox Code Playgroud)

要么

hash_a.deep_merge!(hash_b)
Run Code Online (Sandbox Code Playgroud)


Pau*_*bel 7

如果你将recursive_merge的第一行更改为

merged_hash = merge_to.clone
Run Code Online (Sandbox Code Playgroud)

它按预期工作:

recursive_merge(hash_a, hash_b)    
->    {:a=>{:b=>{:c=>"d", :x=>"y"}}}
Run Code Online (Sandbox Code Playgroud)

在您浏览哈希时更改哈希是很麻烦的,您需要一个"工作区"来累积结果.