2016-08-01 63 views
2

如果我要遞歸合併2個哈希,我可以用下面的函數這樣做:擴展Ruby類(散)的新功能(recursive_merge)

def recursive_merge(a,b) 
    a.merge(b) {|key,a_item,b_item| recursive_merge(a_item,b_item) } 
end 

這個偉大的工程,因爲我現在可以做:

aHash = recursive_merge(aHash,newHash) 

但我想補充一點,這是一種類似於merge!類似的自我更新風格的方法。我可以添加在返回函數:

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"

+1

你試過'self.merge! {| key,a_item,b_item | a_item.recursive_merge!(b_item)}' –

+2

如果您給出了一些散列示例和預期結果,它可以幫助我們。默認情況下'merge'會用相同的鍵代替值,除非你打算做一些不同的事情,塊/遞歸方法可能沒有意義? – Anthony

+0

我猜測他的意思是,如果這些值是散列值,那麼遞歸地合併這些值,而不是簡單地將其中一個覆蓋。 –

回答

1

使用merge!而不是試圖更新self被覆蓋。我不相信使用合併是有意義的!任何地方,但在頂層,所以我不會以遞歸方式調用爆炸版本。相反,使用合併!在頂層,遞歸地調用non-bang方法。

檢查兩個正在合併的值確實是散列也是明智的,否則如果您嘗試對非散列對象執行recursive_merge,則可能會收到異常。

#!/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}} 

正如你可以看到,第二個(共享)H1的下鍵存儲的副本:C被更新以反映h1和h2的下鍵合併:一個。這可能是令人驚訝和不想要的。因此,爲什麼我建議使用recursive_merge進行遞歸,而不是recursive_merge!

+0

好的 - 有道理。爲什麼遞歸函數中的檢查呢?我在沒有檢查的情況下測試了我的用例,它運行得很好,並且想知道你用什麼條件來處理這些'is_a?(Hash)'?標記爲正確。 – Brett

+0

如果您重新使用密鑰,但兩個值都不是散列值,則會引發異常。 –

+1

爲了進一步闡明,'recursive_merge'方法只在'Hash'上定義,所以你只能在hash實例上遞歸。如果值是'Fixnum',就像我的例子中那樣,你會得到一個異常:對於Fixnum,未定義的方法'recursive_merge'。 –