How do I compare two hashes?
Solution 1
You can compare hashes directly for equality:
hash1 = {'a' => 1, 'b' => 2}
hash2 = {'a' => 1, 'b' => 2}
hash3 = {'a' => 1, 'b' => 2, 'c' => 3}
hash1 == hash2 # => true
hash1 == hash3 # => false
hash1.to_a == hash2.to_a # => true
hash1.to_a == hash3.to_a # => false
You can convert the hashes to arrays, then get their difference:
hash3.to_a - hash1.to_a # => [["c", 3]]
if (hash3.size > hash1.size)
difference = hash3.to_a - hash1.to_a
else
difference = hash1.to_a - hash3.to_a
end
Hash[*difference.flatten] # => {"c"=>3}
Simplifying further:
Assigning difference via a ternary structure:
difference = (hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
=> [["c", 3]]
Hash[*difference.flatten]
=> {"c"=>3}
Doing it all in one operation and getting rid of the difference
variable:
Hash[*(
(hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
).flatten]
=> {"c"=>3}
Solution 2
You can try the hashdiff gem, which allows deep comparison of hashes and arrays in the hash.
The following is an example:
a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
b = {a:{y:3}, b:{y:3, z:30}}
diff = HashDiff.diff(a, b)
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]
Solution 3
If you want to get what is the difference between two hashes, you can do this:
h1 = {:a => 20, :b => 10, :c => 44}
h2 = {:a => 2, :b => 10, :c => "44"}
result = {}
h1.each {|k, v| result[k] = h2[k] if h2[k] != v }
p result #=> {:a => 2, :c => "44"}
Solution 4
Rails is deprecating the diff
method.
For a quick one-liner:
hash1.to_s == hash2.to_s
Solution 5
You could use a simple array intersection, this way you can know what differs in each hash.
hash1 = { a: 1 , b: 2 }
hash2 = { a: 2 , b: 2 }
overlapping_elements = hash1.to_a & hash2.to_a
exclusive_elements_from_hash1 = hash1.to_a - overlapping_elements
exclusive_elements_from_hash2 = hash2.to_a - overlapping_elements
dennismonsewicz
Updated on May 28, 2020Comments
-
dennismonsewicz almost 4 years
I am trying to compare two Ruby Hashes using the following code:
#!/usr/bin/env ruby require "yaml" require "active_support" file1 = YAML::load(File.open('./en_20110207.yml')) file2 = YAML::load(File.open('./locales/en.yml')) arr = [] file1.select { |k,v| file2.select { |k2, v2| arr << "#{v2}" if "#{v}" != "#{v2}" } } puts arr
The output to the screen is the full file from file2. I know for a fact that the files are different, but the script doesn't seem to pick it up.
-
dennismonsewicz about 13 yearsIs there anyway to get the differences between the two?
-
PJP over 12 yearsThat only is meaningful if you need the hashes to be identical on the disk. Two files that are different on disk because the hash elements are in different orders, can still contain the same elements, and will be equal as far as Ruby is concerned once they are loaded.
-
davetapley over 11 yearsI had some fairly deep hashes causing test failures. By replacing the
got_hash.should eql expected_hash
withHashDiff.diff(got_hash, expected_hash).should eql []
I now get output which shows exactly what I need. Perfect! -
ohaleck over 9 yearsHashes can be of same size, but contain different values. In such case Both
hash1.to_a - hash3.to_a
andhash3.to_a - hash1.to_a
may return nonempty values thoughhash1.size == hash3.size
. The part after EDIT is valid only if hashes are of different size. -
Jeff Wigal over 9 yearsWow, HashDiff is awesome. Made quick work of trying to see what has changed in a huge nested JSON array. Thanks!
-
Gene about 9 yearsNice, but should have quit while ahead. A.size > B.size doesn't necessarily mean A includes B. Still need to take the union of symmetric differences.
-
Andres almost 9 yearsDiff method is deprecated starting from Rails versions newer than v4.0.2.
-
Alain almost 9 yearsYour gem is awesome! Super helpful when writing specs involving JSON manipulations. Thx.
-
PJP over 7 yearsI always forget about this. There are a lot of equality checks that are made easy using
to_s
. -
aidan about 7 yearsDirectly comparing the output of
.to_a
will fail when equal hashes have keys in a different order:{a:1, b:2} == {b:2, a:1}
=> true,{a:1, b:2}.to_a == {b:2, a:1}.to_a
=> false -
aidan about 7 yearsIt will fail when equal hashes have keys in a different order:
{a:1, b:2} == {b:2, a:1}
=> true,{a:1, b:2}.to_s == {b:2, a:1}.to_s
=> false -
JeremyKun about 7 yearswhat's the purpose of
flatten
and*
? Why not justHash[A.to_a - B.to_a]
? -
Oliver Benning over 6 yearsor
difference.to_h
-
Dave Morse almost 6 yearsWhich is a feature! :D
-
David Bodow almost 6 yearsMy experience with HashDiff has been that it works really well for small hashes but the diff speed doesn't seem to scale well. Worth benchmarking your calls to it if you expect it may get fed two large hashes and making sure that the diff time is within your tolerance.
-
Victor over 4 years@ohaleck You are right! That's why I prefer to use:
hash1.to_a - hash2.to_a | hash2.to_a - hash1.to_a
. Please take a look at my answer => stackoverflow.com/questions/4928789/how-do-i-compare-two-hashes/… -
Eric Walker almost 4 yearsUsing the
use_lcs: false
flag can significantly speed up comparisons on large hashes:Hashdiff.diff(b, a, use_lcs: false)