Check if two arrays have the same contents (in any order)
I'm using Ruby 1.8.6 with Rails 1.2.3, and need to determine whether two arrays have the same elements, regardless of whether or not they're in the same order. One of the arrays is guaranteed not to contain duplicates (the other might, in which case the answer is no).
My first thought was
require 'set'
a.to_set == b.to_set
but I was wondering if there was a more efficient or idiomatic way of doing it.
This doesn't require conversion to set:
a.sort == b.sort
for two arrays A and B:
A and B have same contents if:
(A-B).blank? and (B-A).blank?
or you can just check for:
((A-B) + (B-A)).blank?
Also as suggested by @cort3z this solution als0 works for polymorphic arrays i.e
A = [1 , "string", [1,2,3]]
B = [[1,2,3] , "string", 1]
(A-B).blank? and (B-A).blank? => true
# while A.uniq.sort == B.uniq.sort will throw error `ArgumentError: comparison of Fixnum with String failed`
::::::::::: EDIT :::::::::::::
As suggested in the comments, above solution fails for duplicates.Although as per the question that is not even required since the asker is not interested in duplicates(he is converting his arrays to set before checking and that masks duplicates and even if you look at the accepeted answer he is using a .uniq operator before checking and that too masks duplicates.). But still if duplicates interests you ,Just adding a check of count will fix the same(as per the question only one array can contain duplicates). So the final solution will be:
A.size == B.size and ((A-B) + (B-A)).blank?
Ruby 2.6+
Ruby's introduced difference
in 2.6.
This gives a very fast, very readable solution here, as follows:
a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]
a.difference(b).any?
# => false
a.difference(b.reverse).any?
# => false
a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3]
a.difference(b).any?
# => true
However, the reverse isn't true:
a = [1, 2, 3]
b = [1, 2, 3, 4, 5, 6]
a.difference(b).any?
# => false
This means to get the difference in both directions it is necessary to run:
a.difference(b).any? || b.difference(a).any?
Running the benchmarks:
a = Array.new(1000) { rand(100) }
b = Array.new(1000) { rand(100) }
Benchmark.ips do |x|
x.report('sort') { a.sort == b.sort }
x.report('sort!') { a.sort! == b.sort! }
x.report('to_set') { a.to_set == b.to_set }
x.report('minus') { ((a - b) + (b - a)).empty? }
x.report('difference') { a.difference(b).any? }
x.report('difference two way') { a.difference(b).any? || b.difference(a).any? }
end
sort 10.175k (± 6.2%) i/s - 50.778k in 5.015112s
sort! 10.513k (± 6.8%) i/s - 53.212k in 5.089106s
to_set 4.953k (± 8.8%) i/s - 24.570k in 5.037770s
minus 15.290k (± 6.6%) i/s - 77.520k in 5.096902s
difference 25.481k (± 7.9%) i/s - 126.600k in 5.004916s
difference two way 12.652k (± 8.3%) i/s - 63.232k in 5.038155s
My takeaway would be that difference
is a great choice for a one directional diff.
If you need to check in both directions, it's a balance between performance and readability. For me, the readability pips it, but that's a call to be made on a case by case basis.
Hope that helps someone!