I am looking for a concise way to check a value to see if it is nil or zero. Currently I am doing something like:
if (!val || val == 0)
# Is nil or zero
end
But this seems very clumsy.
I am looking for a concise way to check a value to see if it is nil or zero. Currently I am doing something like:
if (!val || val == 0)
# Is nil or zero
end
But this seems very clumsy.
Objects have a nil? method.
if val.nil? || val == 0
[do something]
end
Or, for just one instruction:
[do something] if val.nil? || val == 0
If you really like method names with question marks at the end:
if val.nil? || val.zero?
# do stuff
end
Your solution is fine, as are a few of the other solutions.
Ruby can make you search for a pretty way to do everything, if you're not careful.
First off I think that's about the most concise way you can check for that particular condition.
Second, to me this is a code smell that indicates a potential flaw in your design. Generally nil and zero shouldn't mean the same thing. If possible you should try to eliminate the possibility of val being nil before you hit this code, either by checking that at the beginning of the method or some other mechanism.
You might have a perfectly legitimate reason to do this in which case I think your code is good, but I'd at least consider trying to get rid of the nil check if possible.
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
:
unless val&.nonzero?
# Is nil or zero
end
Or postfix:
do_something unless val&.nonzero?
You can use the Object.nil? to test for nil specifically (and not get caught up between false and nil). You can monkey-patch a method into Object as well.
class Object
def nil_or_zero?
return (self.nil? or self == 0)
end
end
my_object = MyClass.new
my_object.nil_or_zero?
==> false
This is not recommended as changes to Object are difficult for coworkers to trace, and may make your code unpredictable to others.
nil.to_i returns zero, so I often do this:
val.to_i.zero?
However, you will get an exception if val is ever an object that does not respond_to #to_i.
I believe your code is incorrect; it will in fact test for three values: nil
, false
, and zero. This is because the !val
expression is true for all values that are false, which in Ruby is nil
and false
.
The best I can come up with right now is
if val == nil || val == 0
# do stuff
end
Which of course is not very clever, but (very) clear.
My solution also use Refinements, minus the conditionals.
module Nothingness
refine Numeric do
alias_method :nothing?, :zero?
end
refine NilClass do
alias_method :nothing?, :nil?
end
end
using Nothingness
if val.nothing?
# Do something
end
Rails does this via attribute query methods, where in addition to false and nil, 0 and "" also evaluate to false.
if (model.attribute?) # => false if attribute is 0 and model is an ActiveRecord::Base derivation
However it has its share of detractors. http://www.joegrossberg.com/archives/002995.html
To be as idiomatic as possible, I'd suggest this.
if val.nil? or val == 0
# Do something
end
Because:
Shortest and best way should be
if val&.>(0)
# do something
end
For val&.>(0)
it returns nil when val is nil since > basically is also a method, nil equal to false in ruby. It return false when val == 0
.
Short and clear
[0, nil].include?(val)
I deal with this by defining an "is?" method, which I can then implement differently on various classes. So for Array, "is?" means "size>0"; for Fixnum it means "self != 0"; for String it means "self != ''". NilClass, of course, defines "is?" as just returning nil.
You can use case
if you like:
case val with nil, 0
# do stuff
end
Then you can use anything that works with ===
, which is nice sometimes. Or do something like this:
not_valid = nil, 0
case val1 with *not_valid
# do stuff
end
#do other stuff
case val2 with *not_valid, false #Test for values that is nil, 0 or false
# do other other stuff
end
It's not exactly good OOP, but it's very flexible and it works. My if
s usually end up as case
s anyway.
Of course Enum.any?
/Enum.include?
kind of works too ... if you like to get really cryptic:
if [0, nil].include? val
#do stuff
end
The right thing to do is of course to define a method or function. Or, if you have to do the same thing with many values, use a combination of those nice iterators.
I really like Rails blank?
method for that kind of things, but it won't return true
for 0
. So you can add your method:
def nil_zero?
if respond_to?(:zero?)
zero?
else
!self
end
end
And it will check if some value is nil or 0:
nil.nil_zero?
=> true
0.nil_zero?
=> true
10.nil_zero?
=> false
if val.nil_zero?
#...
end
Instead of monkey patching a class, you could use refinements starting in Ruby 2.1. Refinements are similar to monkey patching; in that, they allow you to modify the class, but the modification is limited to the scope you wish to use it in.
This is overkill if you want to do this check once, but if you are repeating yourself it's a great alternative to monkey patching.
module NilOrZero
refine Object do
def nil_or_zero?
nil? or zero?
end
end
end
using NilOrZero
class Car
def initialize(speed: 100)
puts speed.nil_or_zero?
end
end
car = Car.new # false
car = Car.new(speed: nil) # true
car = Car.new(speed: 0) # true
Refinements were changed in the last minute to be scoped to the file. So earlier examples may have shown this, which will not work.
class Car
using NilOrZero
end
This is very concise:
if (val || 0) == 0
# Is nil, false, or zero.
end
It works as long as you don't mind treating false
the same as nil
. In the projects I've worked on, that distinction only matters once in a while. The rest of the time I personally prefer to skip .nil?
and have slightly shorter code.
[Update: I don't write this sort of thing any more. It works but is too cryptic. I have tried to set right my misdeeds by changing the few places where I did it.]
By the way, I didn't use .zero?
since this raises an exception if val
is, say, a string. But .zero?
would be fine if you know that's not the case.
This evaluates to true for nil and zero: nil.to_s.to_d == 0
unless (val || 0).zero?
# do stufff
end
Another solution:
if val.to_i == 0
# do stuff
end
val ||= 0
if val == 0
# do something here
end