Ruby array equality
I have an array of arrays, called guid_pairs
:
[['a','b','c'],['c','g'],['z','f','b']]
I also have an array, called array_t开发者_如何学Goo_check
:
['c','a','b']
How can I determine if the array guid_pairs
has an element that is equal to array_to_check
. Equality should not consider the position of the array elements.
In this example, the check should return true
because guid_pairs
contains the element ['a','b','c']
, which matches ['c','a','b']
.
I have tried this, but it seems to always return false
even when it should return true
:
guid_pairs.any?{|pair| pair.eql?(array_to_check)}
I am using Ruby 1.9.2
There is a set class in the standard library and using sets nicely matches your intent:
require 'set'
a = ['c','a','b']
aa = [['a','b','c'],['c','g'],['z','f','b']]
find_this = Set.new(a)
the_match = aa.find { |x| find_this == Set.new(x) }
That will leave the matching element element of aa
in the_match
. If you're only interested in existence then you can simply check the truthiness of the_match
; or use any?
(thanks for the reminder Michael Kohl, I often forget about some of the things in Enumerable):
aa.any? { |x| find_this == Set.new(x) }
No tricks, no magic, and using Set makes it clear that you are, in fact, comparing the arrays as sets.
BTW, your attempted solution:
guid_pairs.any? { |pair| pair.eql?(array_to_check) }
doesn't work because arrays compare element-by-element in order so two arrays are equal if and only if they have equal elements in the same order. The documentation for eql?
could be clearer:
Returns true if self and other are the same object, or are both arrays with the same content.
But the ==
documentation is nice and clear:
Two arrays are equal if they contain the same number of elements and if each element is equal to (according to Object.==) the corresponding element in the other array.
We can look to Object#eql? for some clarification though:
The eql? method returns true if obj and anObject have the same value. Used by Hash to test members for equality. For objects of class Object, eql? is synonymous with ==. Subclasses normally continue this tradition, but there are exceptions.
So ==
and eql?
should behave the same way unless there is a good reason for them to be different.
To see if two arrays contain the same elements, regardless of order, you can use the XOR (exclusive or) operation. It will return an array which contains only elements that are in one array and not the other. If the length of the XOR is zero then the input arrays contain the same elements.
def xor(a, b)
(a | b) - (a & b)
end
guid_pairs.any? { |pair| xor(pair, array_to_check).length != 0 }
A possible solution is to sort the arrays before comparing (or even during comparing):
guid_pairs.any?{|pair| pair.sort.eql?(array_to_check.sort)}
Note that this may not be an optimal solution - it would be more appropriate to have your arrays sorted (nevertheless they are sets in your use case).
for equality of two arrays A and B i normally use:
if(((A-B) + (B-A)).blank?)
puts "equal"
else
"unequal"
end
You can use the following:
sorted_array_to_check = array_to_check.sort
guid_pairs.any?{|pair| pair.sort.eql?(sorted_array_to_check)}
Three solutions:
class Array
def check1 other; other.any?{|e| self - e == e - self} end
def check2 other; other.any?{|e| self | e == self and e | self == e} end
def check3 other; other.any?{|e| self & e == self and e & self == e} end
end
array_to_check.check1(guid_pairs) # => true
array_to_check.check2(guid_pairs) # => true
array_to_check.check3(guid_pairs) # => true
Without defining a method (following Josha's suggestion):
array_to_check.instance_eval{guid_pairs.any?{|e| self - e == e - self}} # => true
array_to_check.instance_eval{guid_pairs.any?{|e| self | e == self and e | self == e}} # => true
array_to_check.instance_eval{guid_pairs.any?{|e| self & e == self and e & self == e}} # => true
精彩评论