Let’s say that you have two array of hashes:
a = [
{
initial: "a",
email: "a@example.com"
},
{
email: "b@example.com",
initial: "b"
},
{
initial: "c",
amount: 3,
email: "c@example.com"
}
]
and
b = [
{
email: "c@example.com",
amount: 3,
initial: "c"
},
{
email: "b@example.com",
initial: "b"
},
{
email: "a@example.com",
initial: "a"
}
]
Maybe they are from an import you are doing or maybe you received them via a call from a JSON structure or you just called to_h on an object.
You want to verify if these two arrays contain the same values inside their hash elements and execute a == b.
You will see quickly the response as being false.
Here is the reason from Ruby 3.1 docs (but it works the same also in 3.0, 2.7, 2.6 …):

But you look at the two arrays and see what their values are the same, just that they are not in the same place in both of them.
There are probably multiple solutions to this. I will present two
This solution uses the difference and union to check that the difference between the two arrays does not contain any elements.
The difference between the two arrays will return all elements in the first array that are not present in the second array. See Array#-
So this solution simply makes sure that a - b is empty and b - a is also empty.
You can rewrite this in multiple ways:
((a-b) + (b-a)) == []
(a-b).empty? && (b-a).empty?
((a-b) + (b-a)).empty?
This solution takes into consideration two properties of Set in Ruby:
Set.new with an enumerable as an argument will create a Set containing all items from inside the enumerable object.Based on this you might say that the solution is to simply execute:
Set.new(a) == Set.new(b)
But this has a hidden bug due to another property of a set: one element is included only once in a set, no matter how many times we try to add it.
For example, this scenario will fail:
m = [1,2,2]
n = [2,1]
puts Set.new(m) == Set.new(n) #=> returns true !!!
So the proper solution is the following:
m.length == n.length && Set.new(m) == Set.new(n)