這裏是科林的一個稍作修改的版本。
class Hash
def diff(other)
(self.keys + other.keys).uniq.inject({}) do |memo, key|
unless self[key] == other[key]
if self[key].kind_of?(Hash) && other[key].kind_of?(Hash)
memo[key] = self[key].diff(other[key])
else
memo[key] = [self[key], other[key]]
end
end
memo
end
end
end
遞歸到哈希值進行更有效的左側和右側
{a: {c: 1, b: 2}, b: 2}.diff({a: {c: 2, b: 2}})
回報
{:a=>{:c=>[1, 2]}, :b=>[2, nil]}
,而不是
{:a=>[{:c=>1, :b=>2}, {:c=>2, :b=>2}], :b=>[2, nil]}
好主意科林
這裏是如何的DIFF向原散列
def apply_diff!(changes, direction = :right)
path = [[self, changes]]
pos, local_changes = path.pop
while local_changes
local_changes.each_pair {|key, change|
if change.kind_of?(Array)
pos[key] = (direction == :right) ? change[1] : change[0]
else
path.push([pos[key], change])
end
}
pos, local_changes = path.pop
end
self
end
def apply_diff(changes, direction = :right)
cloned = self.clone
path = [[cloned, changes]]
pos, local_changes = path.pop
while local_changes
local_changes.each_pair {|key, change|
if change.kind_of?(Array)
pos[key] = (direction == :right) ? change[1] : change[0]
else
pos[key] = pos[key].clone
path.push([pos[key], change])
end
}
pos, local_changes = path.pop
end
cloned
end
所以做出向左的樣子正確運行
{a: {c: 1, b: 2}, b: 2}.apply_diff({:a=>{:c=>[1, 2]}, :b=>[2, nil]})
得到
{a: {c: 2, b: 2}, b: nil}
得到確切的說,我們必須走得更遠一點,記錄無鑰匙和無鑰匙之間的區別
,它也將很好,通過只提供添加和刪除來縮短長陣列
爲了我的目的,我並不特別在乎,因爲我真的只需要知道哪些字段已更改。如果我使用AR,這不會是一個問題,但是所有內容都是通過數據層抽象到CouchDB,因此我發現自己需要重新發明輪子,可以說是爲了某些功能。 儘管感謝您的建議。 – Chelsea 2009-11-19 22:02:29
哪一個當然對應於你的「暴力」評論,但我覺得它很有用,而不是那麼可怕或不雅。 – 2009-11-19 22:02:55
這種方法不會注意到'other'哈希中的其他鍵也不能夠告訴鍵沒有值是'nil',用於改進版本檢查http://stackoverflow.com/a/19184270/54247 – dolzenko 2013-10-04 14:52:05