Reputation: 4925
I need to check whether two arrays contain the same data in any order.
Using the imaginary compare
method, I would like to do:
arr1 = [1,2,3,5,4]
arr2 = [3,4,2,1,5]
arr3 = [3,4,2,1,5,5]
arr1.compare(arr2) #true
arr1.compare(arr3) #false
I used arr1.sort == arr2.sort
, which appears to work, but is there a better way of doing this?
Upvotes: 59
Views: 104696
Reputation: 67900
Sorting the arrays prior to comparing them is O(n log n). Moreover, as Victor points out, you'll run into trouble if the array contains non-sortable objects. It's faster to compare histograms, O(n).
In Ruby 2.7+, this can be done with tally:
[1, 2, 1].tally == [2, 1, 1].tally
#=> true
For earlier Ruby versions: You'll find Enumerable#frequency in Facets, but implement it yourself, which is pretty straightforward, if you prefer to avoid adding more dependencies:
require 'facets'
[1, 2, 1].frequency == [2, 1, 1].frequency
#=> true
Upvotes: 38
Reputation: 7310
The easiest way is to use intersections:
@array1 = [1,2,3,4,5]
@array2 = [2,3,4,5,1]
So the statement
@array2 & @array1 == @array2
Will be true
. This is the best solution if you want to check whether array1
contains array2
or the opposite (that is different). You're also not fiddling with your arrays or changing the order of the items.
You can also compare the length of both arrays if you want them to be identical in size:
@array1.size == @array2.size && @array1 & @array2 == @array1
It's also the fastest way to do it (correct me if I'm wrong)
Upvotes: 50
Reputation: 3297
Use difference method if length of arrays are the same https://ruby-doc.org/core-2.7.0/Array.html#method-i-difference
arr1 = [1,2,3]
arr2 = [1,2,4]
arr1.difference(arr2) # => [3]
arr2.difference(arr1) # => [4]
# to check that arrays are equal:
arr2.difference(arr1).empty?
Otherwise you could use
# to check that arrays are equal:
arr1.sort == arr2.sort
Upvotes: 0
Reputation: 881
You can open array
class and define a method like this.
class Array
def compare(comparate)
to_set == comparate.to_set
end
end
arr1.compare(arr2)
irb => true
OR use simply
arr1.to_set == arr2.to_set
irb => true
Upvotes: 3
Reputation: 2783
The most elegant way I have found:
arr1 = [1,2,3,5,4]
arr2 = [3,4,2,1,5]
arr3 = [3,4,2,1,5,5]
(arr1 - arr2).empty?
=> true
(arr3 - arr2).empty?
=> false
Upvotes: 4
Reputation: 1042
Here is a version that will work on unsortable arrays
class Array
def unordered_hash
unless @_compare_o && @_compare_o == hash
p = Hash.new(0)
each{ |v| p[v] += 1 }
@_compare_p = p.hash
@_compare_o = hash
end
@_compare_p
end
def compare(b)
unordered_hash == b.unordered_hash
end
end
a = [ 1, 2, 3, 2, nil ]
b = [ nil, 2, 1, 3, 2 ]
puts a.compare(b)
Upvotes: 0
Reputation: 11056
If you know that there are no repetitions in any of the arrays (i.e., all the elements are unique or you don't care), using sets is straight forward and readable:
Set.new(array1) == Set.new(array2)
Upvotes: 27
Reputation: 1026
You can actually implement this #compare
method by monkey patching the Array class like this:
class Array
def compare(other)
sort == other.sort
end
end
Keep in mind that monkey patching is rarely considered a good practice and you should be cautious when using it.
There's probably is a better way to do this, but that's what came to mind. Hope it helps!
Upvotes: 6