Related
I'm new to Ruby, and trying to understand why this works (it does seem to, at least according to "the Master"):
def array_of_fixnums?(array)
true unless array.find { |item| item.class != Fixnum }
end
My concern is where the "false-ness" is coming from when the array contains non-fixnum values. Am I right to assume there is no implicit "else false" in the unless statement? In that case I assume it must be coming from the nil value returned by Enumerable#find. Is that correct?
If so, that seems a bit shaky. Might it be better to return false explicitly, like this?
array.find { |item| item.class != Fixnum } ? false : true
Is there another, better way entirely? Thanks for helping me wrap my head around this, and for any "best practice" suggestions.
Your method is returning nil not because find returns nil, but because if your inline conditional does not pass, the method has no explicit return value. This would be more clear if it were not inline, consider:
def array_of_fixnums?(array)
unless array.find { |item| item.class != Fixnum }
return true
end
# otherwise don't explicitly return (implicit nil)
end
While relying on the falsiness of nil will often work, it is problematic in that it does not follow the principle of least surprise. A ? method should return true or false.
But your method has worse problems. It uses confusing logic (a double negative), and itself relies on the falsiness of nil and the truthiness of not nil, to function. Consider what happens if your method were passed [false]. Oops.
The better way would be something like:
array.all? {|n| n.is_a? Fixnum }
The reasoning is that this method does exactly what it says, plainly.
Returning a boolean explicitly, while not necessarily wrong, is superfluous and often considered bad practice. Rather consider the example, which says, in ruby speak, is every one of the values in this array a Fixnum?. The result of that expression is what the method is after; there's no reason to evaluate it then return true|false.
From the find method doc:
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
Thus, all you need is:
def array_of_fixnums?(array)
array.find { |item| item.class != Fixnum }
end
If anything is found, that will be returned, otherwise nil will be returned, and the method will evaluate do false.
However, as the item return could be an false or nil (if any item in the list is false or nil) I would recommend that you use the .any? method instead.
def array_of_fixnums?(array)
array.any? { |item| item.class != Fixnum }
end
Let's look at why true unless condition works as it does.
I believe x unless condition was provided as an esthetic improvement to x if !condition, but the two are equivalent (and, in my opinion, it is an improvement). To get rid of double-negatives, I'll just consider x if condition.
One of Ruby's basic tenets is that every statement must return a value (namely, the last expression evaluated). Therefore, x if condition must be evaluated as if condition then x else y end. But what is y? If y evaluated as true or false, x if condition would be meaningless when x is a boolean expression. To see why, consider:
w = (x == 5 if z == 6)
If w were true, we could conclude that x = 5 and z = 6, but if w were false, we wouldn't know whether z = 6 and x != 5, or z != 6 and x = 5 or x != 5. Considering that any value of y other than nil is evaluated as true or false, the only reasonable value for y is nil. That way, if w == nil is be available. Of course, this is still a problem (in other situations) when x could possibly be nil.
I would welcome clarification and elaboration. Perhaps there is also a less convoluted way of making this argument.
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?
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 }.
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 }
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?
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.
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.)
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.
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.
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"
By noticing that existential quantification is equivalent to disjunction, you can also see that you should expect false with existential quantification over an empty set.
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.
Make sure the array is not empty first.
Then:
array.compact.present? && array.all? {|x| x != 2}
I have a big array and I need to know whether all its elements are divisible by 2.
I'm doing it this way, but it's sort of ugly:
_true = true
arr.each { |e| (e % 2).zero? || _true = false }
if _true == true
# ...
end
How to do this without extra loops/assignments?
This will do.
arr.all?(&:even?)
Ruby's got you covered.
if arr.all? {|e| (e % 2).zero?}
There's also any? if you need to check whether at least one element has a given property.
I have an array, if I find a value in it, I want to execute a block of code. Also, if the array is nil, I want to execute that block. So the code I tried is:
if !array.respond_to? :index || array.index(str)
#some code
So if it's nil it's true, or if str is somewhere in the array, it's true, right? But if it finds the item at index 0, it doesn't enter the block. Also, according to irb false || 0 evalueates to 0. WTF?? I thought that everything was true except false and nil. I guess || does something odd that I'm not expecting??
My questions are: What's going on? What's a nice way to write a conditional that does what I want?
Using nil? and include? with an inline if seems most idiomatic to me.
#your code if arr.nil? || arr.include?(str)
if array.nil? || array.member?(s)
# ...
false || 0 evaluates to 0 because it's an or. False isn't truthy (obviously ;) but 0 is, so the expression is truthy.
Are you checking for a nil array or an empty one? If you've already declared the array it won't be nil even if it's empty. I'd write it like:
if array.empty? || array.include(str)
or if you really want to check for a nil array:
if array.nil? || array.include(str)
I'd use .include rather than .index to avoid getting a 0.
if array.nil? || array.member?(str)
#code block
end
The || operator almost reminds you of a coalesce.
Given a = false, b = :bacon
return a || b #returns :bacon
I am using the following code to check if a variable is not nil and not zero
if(discount != nil && discount != 0)
...
end
Is there a better way to do this?
unless discount.nil? || discount == 0
# ...
end
class Object
def nil_zero?
self.nil? || self == 0
end
end
# which lets you do
nil.nil_zero? # returns true
0.nil_zero? # returns true
1.nil_zero? # returns false
"a".nil_zero? # returns false
unless discount.nil_zero?
# do stuff...
end
Beware of the usual disclaimers... great power/responsibility, monkey patching leading to the dark side etc.
ok, after 5 years have passed....
if discount.try :nonzero?
...
end
It's important to note that try is defined in the ActiveSupport gem, so it is not available in plain ruby.
From Ruby 2.3.0 onward, you can combine the safe navigation operator (&.) with Numeric#nonzero?. &. returns nil if the instance was nil and nonzero? - if the number was 0:
if discount&.nonzero?
# ...
end
Or postfix:
do_something if discount&.nonzero?
unless [nil, 0].include?(discount)
# ...
end
You could do this:
if (!discount.nil? && !discount.zero?)
The order is important here, because if discount is nil, then it will not have a zero? method. Ruby's short-circuit evaluation should prevent it from trying to evaluate discount.zero?, however, if discount is nil.
if (discount||0) != 0
#...
end
You can convert your empty row to integer value and check zero?.
"".to_i.zero? => true
nil.to_i.zero? => true
if discount and discount != 0
..
end
update, it will false for discount = false
You can take advantage of the NilClass provided #to_i method, which will return zero for nil values:
unless discount.to_i.zero?
# Code here
end
If discount can be fractional numbers, you can use #to_f instead, to prevent the number from being rounded to zero.
def is_nil_and_zero(data)
data.blank? || data == 0
end
If we pass "" it will return false whereas blank? returns true.
Same is the case when data = false
blank? returns true for nil, false, empty, or a whitespace string.
So it's better to use blank? method to avoid empty string as well.
I prefer using a more cleaner approach :
val.to_i.zero?
val.to_i will return a 0 if val is a nil,
after that, all we need to do is check whether the final value is a zero.
Yes, we do have a clean way in ruby.
discount.to_f.zero?
This check handles good amount of cases i.e. discount may be nil, discount may be int 0, discount may be float 0.0, discount may be string "0.0", "0".
When dealing with a database record, I like to initialize all empty values with 0, using the migration helper:
add_column :products, :price, :integer, default: 0
if discount.nil? || discount == 0
[do something]
end
You could initialize discount to 0 as long as your code is guaranteed not to try and use it before it is initialized. That would remove one check I suppose, I can't think of anything else.
Alternative solution is to use Refinements, like so:
module Nothingness
refine Numeric do
alias_method :nothing?, :zero?
end
refine NilClass do
alias_method :nothing?, :nil?
end
end
using Nothingness
if discount.nothing?
# do something
end
I believe the following is good enough for ruby code. I don't think I could write a unit test that shows any difference between this and the original.
if discount != 0
end