Reputation: 12663
Using Ruby I want to evaluate all items in an array, and return true if they all pass a conditional test.
I can do this using e.g. array.all? { |value| value == 2 }
So:
> array=[2,2]
> array.all? { |value| value == 2 }
=> true
> array=[2,3]
> array.all? { |value| value == 2 }
=> false
Great!
But, why does an empty array pass this test?
> array=[]
> array.all? { |value| value == 2 }
=> true
Shouldn't this return false?
And if I need it to return false, how should I modify the method?
Upvotes: 32
Views: 8023
Reputation: 867
Make sure the array is not empty first. Then:
array.compact.present? && array.all? {|x| x != 2}
Upvotes: 0
Reputation: 7225
In Ruby you can never loop over an empty collection (array, hashes, etc.), so in your case your block never gets executed. And if the block never gets executed, all?
returns true (there is no condition to make the result false).
Read about all?
in the Ruby documentation.
You can simply achieve your goal by
!array.empty? && array.all? { |value| value == 2 }
Upvotes: 11
Reputation: 162
The source of all? method says that it uses static variable(which is initially set to true) and then performs the AND operation between the static variable value and the result of the iteration finally returns this static variable as a result.
as the array is Empty ruby will never iterate on this empty array and as a result of this all? method will return the static variable which was set to true.
Upvotes: 0
Reputation: 6567
Just go
!(array.empty? || array.any? {|x| x != 2})
(Which has the added advantage of failing fast—that is, it can be evaluated properly without having to scan the whole array.)
Upvotes: 1
Reputation: 18577
This is a vacuous truth. It's the standard interpretation of a universal quantification, i.e. a
collection.all? { |x| some_predicate(x) }
over an empty collection
, but it's known to strike people as counter-intuitive when they first see it in a formal setting. One nice way to think about why this is the preferred semantics is to think about how you would implement all?
.
To make your test require that the array is non-empty, just do
array.any? && array.all? { |x| x == 2 }
Note that array.any?
is fast no matter how large the array, whereas array.all? { |x| x == 2 }
can be slow, depending on how big array
is and how rare 2
is in it. So put the array.any?
first.
Also note, there are degenerate cases where this won't work, for instance if array
is [nil]
or [false]
. If cases like this might come up, replace array.any?
with array.any? { true }
.
Upvotes: 34
Reputation: 168169
As Amit Kumar Gupta writes, it is the standard interpretation of universal quantification. I have no idea why you expect it to be false
. Here, you can see it should be true
by inference.
Universal quantification is equivalent to conjunction, thus ("<=>" means equivalent):
"for all x in [a, b, c], P(x)" <=> "P(a) and P(b) and P(c)"
Notice that any proposition is equivalent to the conjunction of true and itself, so:
"for all x in [a, b, c], P(x)" <=> "true and P(a) and P(b) and P(c)"
If you lessen the elements in the set to two, you get:
"for all x in [a, b], P(x)" <=> "true and P(a) and P(b)"
and further to one element:
"for all x in [a], P(x)" <=> "true and P(a)"
Now, what happens with the empty set? Naturally,
"for all x in [], P(x)" <=> "true"
false
with existential quantification over an empty set.
Upvotes: 0
Reputation: 14913
The documentation says : "The method returns true if the block never returns false or nil.."
In the case of an empty array the block never executes and hence the method will always return true
. As far as returning false
is concerned you'll have to arr.empty?
Upvotes: 5
Reputation: 12578
Zeroes, empty collections, empty matrices and such have always been a bit special, if not outright problematic. Greeks knew well why they didn't count 0 among natural integers.
Method all?
would be the first to ask you "why are you calling me on an empty array?" What do you mean by "all?", when there is nothing in there? That's a contradiction. And the method does short thinking, and answers true
for the reasons outlined in the other three answers. Remember, you are at fault for talking about "all elements" of an empty array to begin with.
Upvotes: 0
Reputation: 10799
Since there is no item in the array that FAILS that test, it returns true. So just use somehting like:
array.size > 0 and array.all? { |value| value == 2}
Or something like that.
Upvotes: 0
Reputation: 129802
There is no item in that array that doesn't pass the test. I think you may need to throw in a test for array length.
Upvotes: 4