Comparing ruby hashes [duplicate] Comparing ruby hashes [duplicate] ruby ruby

Comparing ruby hashes [duplicate]


here is a slightly modified version from colin's.

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  endend

It recurses into the hashes for more efficient left and right

{a: {c: 1, b: 2}, b: 2}.diff({a: {c: 2, b: 2}})

returns

{:a=>{:c=>[1, 2]}, :b=>[2, nil]}

instead of

{:a=>[{:c=>1, :b=>2}, {:c=>2, :b=>2}], :b=>[2, nil]}

Great idea colin

here is how to apply the diff to the original hashes

  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 

so to make the left look like the right you run

{a: {c: 1, b: 2}, b: 2}.apply_diff({:a=>{:c=>[1, 2]}, :b=>[2, nil]})

to get

{a: {c: 2, b: 2}, b: nil}

to get exact we would have to go a little farther and record a difference between between nil and no key
and it would also be nice to shorten long arrays by just providing adds and removes


Edit:

I keep coming back to this code to use it in projects I'm in. Here's the latest which is useful for deeply nested structures and based on Pete's code above. I usually drop it in config/initializers/core_ext.rb (in a Rails project):

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  endendclass 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  endend

Here's a small demo:

> {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"]}}}

Older response:

I have found Rails' Hash diff method to not actually tell me what was on the left side and right side (which is far more useful). There was a plugin call "Riff", that has since disappeared, which would let you diff two ActiveRecord objects. Essentially:

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  endend


If all you care about is what's unique in element2, you can just do:

element2.to_a - element1.to_a