可能性のある複製:
2つのハッシュを比較するにはどうすればよいですか?
2つのRubyハッシュ(基本的にはモデルです)があり、それらの違いを見つけようとしています。1つはオブジェクトの古いインスタンスで、もう1つはいくつかの属性に新しい値が割り当てられています。私は変更されたキーを特定しようとしていますが、そのためにハッシュに組み込まれているものは何もないようです。いくつかのブルートフォースソリューションが考えられますが、おそらくそこにエレガントなソリューションがあるのではないかと考えていました。
理想的には、次のように2つのハッシュを取得できる必要があります。
element1 = {:name => "Original", :description => "The original one!"}
element2 = {:name => "Original", :description => "The new one!"}
そして、それらを比較/比較して、次のようなものを取り戻すことができます:
{:description => "The new one!"}
現在、私が本当に考えることができるのは、1つのハッシュのキーを反復処理し、そのキーの値を2番目のハッシュの対応するキーと比較することだけですが、それは強引すぎるようです。
何か案は?どうもありがとう!
これは、colinのものを少し変更したバージョンです。
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]}
素晴らしいアイデアコリン
これは、元のハッシュに差分を適用する方法です
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}
正確に説明するには、もう少し進んで、nilとno keyの違いを記録する必要があります
また、追加と削除を提供するだけで長い配列を短くするのもいいでしょう
編集:
私はこのコードに戻ってきて、自分がいるプロジェクトでそれを使用します。深くネストされた構造に役立ち、上記のピートのコードに基づく最新のコードを次に示します。私は通常それをconfig/initializers/core_ext.rbにドロップします(Railsプロジェクト):
class Hash
def deep_diff(other)
(self.keys + other.keys).uniq.inject({}) do |memo, key|
left = self[key]
right = other[key]
next memo if left == right
if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
memo[key] = left.deep_diff(right)
else
memo[key] = [left, right]
end
memo
end
end
end
class Array
def deep_diff(array)
largest = [self.count, array.count].max
memo = {}
0.upto(largest - 1) do |index|
left = self[index]
right = array[index]
next if left == right
if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
memo[index] = left.deep_diff(right)
else
memo[index] = [left, right]
end
end
memo
end
end
ここに小さなデモがあります:
> {a: [{b: "c", d: "e"}, {b: "c", f: "g"}]}.deep_diff({a: [{b: "c", d: "e"}, {b: "d", f: "g"}]})
=> {:a=>{1=>{:b=>["c", "d"]}}}
古い応答:
RailsのHash diffメソッドは、左側と右側に何があったかを実際に教えないことがわかりました(これははるかに便利です)。プラグインコール "Riff"がありましたが、それは消えてしまったため、2つのActiveRecordオブジェクトを比較できます。基本的に:
class Hash
def diff(other)
self.keys.inject({}) do |memo, key|
unless self[key] == other[key]
memo[key] = [self[key], other[key]]
end
memo
end
end
end
Element2のユニークな点だけが気になる場合は、次のようにすることができます。
element2.to_a - element1.to_a