使用新函数(递归合并)扩展ruby类(哈希)

使用新函数(递归合并)扩展ruby类(哈希),ruby,Ruby,如果要递归合并2个哈希,可以使用以下函数: def recursive_merge(a,b) a.merge(b) {|key,a_item,b_item| recursive_merge(a_item,b_item) } end class Hash def recursive_merge(newHash) self.merge { |key,a_item,b_item| a_item.recursive_merge(b_item) } end end 这非常有效,因为

如果要递归合并2个哈希,可以使用以下函数:

def recursive_merge(a,b)
  a.merge(b) {|key,a_item,b_item| recursive_merge(a_item,b_item) }
end
class Hash
  def recursive_merge(newHash)
    self.merge { |key,a_item,b_item| a_item.recursive_merge(b_item) }
  end
end
这非常有效,因为我现在可以:

aHash = recursive_merge(aHash,newHash)
但我想将其添加为类似于
merge的自更新样式方法。我可以添加返回功能:

def recursive_merge(a,b)
  a.merge(b) {|key,a_item,b_item| recursive_merge(a_item,b_item) }
end
class Hash
  def recursive_merge(newHash)
    self.merge { |key,a_item,b_item| a_item.recursive_merge(b_item) }
  end
end
但是我不知道如何重新创建
bang
函数,该函数在没有关联的情况下更新原始对象

class Hash
  def recursive_merge!(newHash)
    self.merge { |key,a_item,b_item| a_item.recursive_merge(b_item) }
    # How do I set "self" to this new hash?
  end
end
根据评论编辑示例

h={:a=>{:b => "1"}
h.recursive_merge!({:a=>{:c=>"2"})
 => {:a=>{:b=>"1", :c="2"}}

常规合并会导致
:b=>“1”
:c=“2”
使用
合并而不是尝试更新
self
。我认为使用合并是没有意义的!除了顶层之外的任何地方,所以我不会递归地调用bang版本。相反,使用合并!并递归调用non-bang方法

检查合并的两个值是否确实是散列也是明智的,否则,如果尝试对非散列对象进行递归合并,可能会出现异常

#!/usr/bin/env ruby

class Hash
  def recursive_merge(other)
    self.merge(other) { |key, value1, value2| value1.is_a?(Hash) && value2.is_a?(Hash) ? value1.recursive_merge(value2) : value2}
  end

  def recursive_merge!(other)
    self.merge!(other) { |key, value1, value2| value1.is_a?(Hash) && value2.is_a?(Hash) ? value1.recursive_merge(value2) : value2}
  end
end


h1 = { a: { b:1, c:2 }, d:1 }
h2 = { a: { b:2, d:4 }, d:2 }
h3 = { d: { b:1, c:2 } }


p h1.recursive_merge(h2) # => {:a=>{:b=>2, :c=>2, :d=>4}, :d=>2}
p h1.recursive_merge(h3) #  => {:a=>{:b=>1, :c=>2}, :d=>{:b=>1, :c=>2}}

p h1.recursive_merge!(h2) # => {:a=>{:b=>2, :c=>2, :d=>4}, :d=>2}
p h1 # => {:a=>{:b=>2, :c=>2, :d=>4}, :d=>2}
如果您有一个特定的原因需要就地完全合并(可能是为了速度),那么您可以尝试递归地调用第二个函数本身,而不是将递归委托给第一个函数。请注意,如果哈希存储共享对象,可能会产生意外的副作用

例如:

h1 = { a:1, b:2 }
h2 = { a:5, c:9 }
h3 = { a:h1, b:h2 }
h4 = { a:h2, c:h1 }

p h3.recursive_merge!(h4)
# Making recursive calls to recursive_merge
# => {:a=>{:a=>5, :b=>2, :c=>9}, :b=>{:a=>5, :c=>9}, :c=>{:a=>1, :b=>2}}
# Making recursive calls to recursive_merge!
# => {:a=>{:a=>5, :b=>2, :c=>9}, :b=>{:a=>5, :c=>9}, :c=>{:a=>5, :b=>2, :c=>9}}

如您所见,存储在key:c下的h1的第二个(共享)副本被更新,以反映在key:a下的h1和h2的合并。这可能是令人惊讶和不必要的。因此,我建议对递归使用
recursive\u merge
,而不是
recursive\u merge

您是否尝试了
self.merge!{| key,a|u item,b|u item | a|u item.recursive_merge!(b|item)}
如果您给出一些哈希示例和预期结果,将对我们有所帮助<“代码>合并”
默认情况下,将用相同的键替换值,因此除非您计划以不同的方式执行块/递归方法,否则这可能没有意义?我可能猜他的意思是,如果值是散列,则递归合并这些值,而不是简单地用另一个覆盖其中一个。@JagdeepSingh非常明显-我没有。谢谢你的反馈。啊,好的,有道理。为什么要在递归函数中进行检查?我在没有检查的情况下用我的用例进行了测试,它也运行得很好,我想知道您使用那些
is_a?(Hash)
?标记为正确。如果重复使用一个键,但两个值都不是散列,则会引发异常。为了进一步澄清,
recursive\u merge
方法仅在
Hash
上定义,因此只能在散列实例上递归。如果值是
Fixnum
s,如我的示例所示,您将得到en异常:Fixnum的未定义方法“recursive\u merge”。