How to Compare Two Ruby Hashes and Find the Difference
You have two hashes and you need to know how they differ. Maybe one is the expected result in a test and the other is what your code produced. Maybe one is yesterday’s API response and the other is today’s. Either way, “are these the same, and if not, what changed?” is a question Ruby can answer in several ways. Here are the options, from simplest to most thorough.
Equality: are they the same at all?
The first question is just whether the two hashes are equal. Ruby’s == compares hashes by content, not order:
a = { name: "Alice", role: "admin" }
b = { role: "admin", name: "Alice" }
a == b # => true (order does not matter for ==)
This tells you whether they differ, but not how. For small hashes that might be all you need. For anything larger, you want the specific differences.
Finding changed and missing keys with plain Ruby
Ruby’s Hash and Set give you enough to compute differences by hand.
Which keys exist in one but not the other
expected = { id: 1, name: "Alice", role: "admin" }
actual = { id: 1, name: "Alice", plan: "pro" }
expected.keys - actual.keys # => [:role] (missing from actual)
actual.keys - expected.keys # => [:plan] (extra in actual)
Which shared keys have different values
shared = expected.keys & actual.keys
changed = shared.reject { |k| expected[k] == actual[k] }
changed.map { |k| [k, expected[k], actual[k]] }
# => [] (in this case the shared keys all match)
A one-shot difference of two flat hashes
Combining the ideas, you can build a small diff:
def hash_diff(a, b)
(a.keys | b.keys).each_with_object({}) do |key, diff|
next if a[key] == b[key]
diff[key] = { expected: a[key], actual: b[key] }
end
end
hash_diff(expected, actual)
# => {:role=>{:expected=>"admin", :actual=>nil},
# :plan=>{:expected=>nil, :actual=>"pro"}}
This works well for flat hashes. It is worth keeping in a test helper. The limitation shows up with nesting.
The hard case: nested hashes
Real data is rarely flat. Once you have hashes inside hashes inside arrays, a shallow == per key stops being enough, because two nested hashes are only == if everything inside them matches, and when they do not, plain Ruby just tells you the whole subtree differs without saying where.
a = { user: { name: "Alice", address: { city: "Melbourne" } } }
b = { user: { name: "Alice", address: { city: "Sydney" } } }
a == b # => false, but where?
You can write a recursive diff that walks both structures in parallel, and it is a good exercise, but it is fiddly to get right: you have to handle keys present on one side only, arrays of different lengths, and type mismatches at every level. For a one-off debugging session, writing that code is slower than the problem you are trying to solve.
The fast path: a visual side-by-side diff
When you just need to see the difference, especially for nested or large hashes, a visual diff is the quickest route. RubyHash is built for exactly this. Paste both hashes, in Ruby syntax straight from a console or test, and it:
- parses each into a real structure,
- sorts keys so ordering differences do not show up as fake changes,
- renders a side-by-side comparison with the differing values highlighted, and
- marks type changes explicitly, so a
1that became"1"is called out rather than hidden.
For the nested example above, you would see the diff drill straight down to user.address.city changing from Melbourne to Sydney, with everything else greyed out as unchanged. No recursive diff code, no mental bookkeeping of brace levels.
It runs in your browser and sends nothing to a server, so it is safe to use with real data from a failing test.
Which approach should you use?
- Just need a yes or no? Use
==. - Flat hashes, want it in code? The
hash_diffhelper above is small and reusable, good for a test assertion or a script. - Nested, large, or you are debugging interactively? Paste them into RubyHash and read the difference directly. It is faster than writing and debugging a recursive diff in the moment.
The right tool depends on whether you need the comparison in code (write the helper) or you need to see it now (use the visual diff). Most debugging sessions are the second kind, which is exactly why the tool exists.
Comparing Ruby hashes and need to spot the difference fast? Try RubyHash, paste your two hashes for a clean, side-by-side diff.
Enjoyed this post?
Subscribe to get notified when we publish more Ruby and Rails content.