Skip to content

Instantly share code, notes, and snippets.

@cantino
Created September 21, 2015 21:23
Show Gist options
  • Save cantino/7c2251bb0a9721f67bcc to your computer and use it in GitHub Desktop.
Save cantino/7c2251bb0a9721f67bcc to your computer and use it in GitHub Desktop.
Recursive Ruby data structure diff
module ObjectComparisonHelper
def deep_hash_diff(obj1, obj2, path = 'root', differences = [])
if obj1.class != obj2.class
differences << "object types differ at #{path}: #{obj1.class} vs #{obj2.class}"
else
case obj1
when Hash, HashWithIndifferentAccess
differences << "key sets differ at #{path}: #{obj1.keys.inspect} vs #{obj2.keys.inspect}" if obj1.keys.to_set != obj2.keys.to_set
obj1.each do |key, value|
deep_hash_diff(value, obj2[key], "#{path}.#{key}", differences)
end
when Array
differences << "array lengths differ at #{path}: #{obj1.length} vs #{obj2.length}" if obj1.length != obj2.length
obj1.zip(obj2).each.with_index do |(item1, item2), index|
deep_hash_diff(item1, item2, "#{path}[#{index}]", differences)
end
else
differences << "values differ at #{path}: #{obj1} vs #{obj2}" if obj1 != obj2
end
end
differences
end
end
require 'spec_helper'
describe ObjectComparisonHelpers do
let(:helper) do
Class.new { include ObjectComparisonHelpers }.new
end
describe '#deep_hash_diff' do
it 'outputs an array of difference information' do
obj1 = {
'key' => 'value',
:hash => { foo: :bar, counter: 1 },
:type => Time.now,
:array => [1,2,3]
}
obj2 = {
'key' => 'value',
:hash => { foo: :bar, counter: 2 },
:type => 'string',
:array => [1,2,3,4],
'spurious' => 'value'
}
helper.deep_hash_diff(obj1, obj2).should == [
"key sets differ at root: [\"key\", :hash, :type, :array] vs [\"key\", :hash, :type, :array, \"spurious\"]",
"values differ at root.hash.counter: 1 vs 2",
"object types differ at root.type: Time vs String",
"array lengths differ at root.array: 3 vs 4"
]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment