85

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.

JosephRuby
  • 475
  • 3
  • 10
csexton
  • 24,061
  • 15
  • 54
  • 57

22 Answers22

72

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
user513951
  • 12,445
  • 7
  • 65
  • 82
Christian Lescuyer
  • 18,893
  • 5
  • 49
  • 45
  • 5
    watch out for use of "or" and "and", btw, they have different and very low precedence compared to || and &&. See http://blog.jayfields.com/2007/08/ruby-operator-precedence-of-and-which.html for some remarks. – Mike Woodhouse Oct 16 '08 at 19:26
  • I agree with some poster of the "smell" in the question, however to me this is more the ruby way. – Jonke Oct 16 '08 at 20:20
  • 6
    The precedence of `or` does not require any parens here, and will read completely naturally to an experienced Rubyist. The only people who are ever bitten by `||` versus `or` are people who have never programmed Perl :) – ephemient Oct 16 '08 at 21:42
  • 1
    The only time you need to worry about || versus OR precedence is in assignment, not boolean checking. However, the low precedence of the word can be very handy for error checking. – Robert K Nov 12 '08 at 15:20
  • 3
    I think `val && val == 0` is better. – Nakilon Sep 24 '10 at 21:42
  • It happens that `nil.to_i == 0`, hence, I'd suggest: `val.to_i.zero?` – Augustin Riedinger Feb 13 '19 at 12:16
43

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?
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • 1
    This was a terrific new and modern answer. Also, there's a great article on why using it here: [The Safe Navigation Operator (&.) in Ruby](http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/) – Michael Gaskill May 07 '16 at 05:20
  • 1
    Very concise, but without the comment it would take me a moment to understand that `unless val&.nonzero?` translates to "if val is nil or zero" – Stefan Jun 27 '17 at 09:40
  • 1
    @Stefan, agreed. I wouldn't use this in production code myself. I included it for completionism. The opposite is perfectly readable though - `do_something if val&.nonzero?` (aka if `val` is not `nil` and not zero). – ndnenkov Jun 27 '17 at 09:56
  • Thanks for this. Still works as of ruby 2.7.7. I was able to use your solution to fix my ternary statement `my_value&.nonzero? ? do_something : do_something_else` – B. Bulpett May 08 '23 at 15:23
38

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.

30

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.

Mike Deck
  • 18,045
  • 16
  • 68
  • 92
  • 4
    Very right about the code smell! +1 Might consider looking into NullObject pattern http://en.wikipedia.org/wiki/Null_Object_pattern – abyx Nov 06 '09 at 19:57
10

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.

ntl
  • 119
  • 1
  • 2
  • for reference: [Scott's answer](https://stackoverflow.com/a/480206/2799887) and what @AndrewGrimm said about it: `"5".to_i == 5, but you're more or less right. Clever but doesn't actually work` – pjvleeuwen May 05 '19 at 12:49
9

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.

Adrian Dunston
  • 2,950
  • 4
  • 24
  • 23
  • I would modify the Numeric class rather than Object. – epochwolf Nov 05 '08 at 00:24
  • 6
    epochwolf, if you put this on the Numeric class, then `nil?` would always return false. `nil?` only returns true on the NilClass. – Ryan McGeary Jan 26 '09 at 02:21
  • You could implement the method in `Object`, `NilClass` and `Numeric`. `Object` would return `false`, `NilClass` would return `true` and `Numeric` would return `zero?`. – Stefan Jun 27 '17 at 05:25
4

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.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • you can probably assume that the variable in question is a Numeric. If you don't even know whether it's a Boolean or a Numeric there are bigger problems in the code. – Mike Deck Oct 16 '08 at 17:32
4

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
RichOrElse
  • 114
  • 5
3

Short and clear

[0, nil].include?(val)

Stanislav Kr.
  • 504
  • 4
  • 12
  • 3
    Can also reverse that: `val.in? [0,nil]` – mahemoff Nov 29 '16 at 01:04
  • 1
    @mahemoff that is certainly the best idiom for it! – intmarinoreturn0 Mar 20 '19 at 13:13
  • @mahemoff better use include?,since its faster than in? ref:https://stackoverflow.com/questions/28272550/difference-between-in-and-include-in-rails – Lakhani Aliraza Sep 06 '19 at 08:40
  • Good point, but I ran it just now on modern Ruby and in? was actually faster for a single invocation, and for 1000 invocations, they are quite similar. Both are blazing-fast anyway, enough that few applications would be affected imo. https://gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8 – mahemoff Sep 06 '19 at 14:01
3

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.

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
2

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

Gishu
  • 134,492
  • 47
  • 225
  • 308
  • Link doesn't work. I'm curious about the method `#attribute`, cause I can't find anything like that. – mrzasa Aug 07 '12 at 07:00
  • It is something that Rails/ActiveRecord adds on the fly. If Customer has a name field, name? is auto-added to check if the name is blank or nil. See http://api.rubyonrails.org/classes/ActiveRecord/Base.html - search for section 'attribute query methods' – Gishu Aug 07 '12 at 08:48
2

To be as idiomatic as possible, I'd suggest this.

if val.nil? or val == 0
    # Do something
end

Because:

  • It uses the nil? method.
  • It uses the "or" operator, which is preferable to ||.
  • It doesn't use parentheses, which are not necessary in this case. Parentheses should only be used when they serve some purpose, such as overriding the precedence of certain operators.
Joshua Swink
  • 3,380
  • 3
  • 29
  • 27
  • 1
    Although I know this is from literally 7 years ago, I downvoted because of this: https://github.com/bbatsov/ruby-style-guide#no-and-or-or – Devon Parsons Jan 23 '15 at 13:44
0

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.

glenn mcdonald
  • 15,290
  • 3
  • 35
  • 40
0

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
klew
  • 14,837
  • 7
  • 47
  • 59
0

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.

antinome
  • 3,408
  • 28
  • 26
  • `[edited]` I thought that if `val` was nil then this expression would evaluate to `nil == 0` (and thus return `false`). However it appears it does actually work, although I don't think it is very readable. – omnikron Jan 25 '16 at 13:09
  • I now believe my approach here was too cryptic, just to save a few characters, and I wouldn't write it like this nowadays. So I don't expect any upvotes, but I am a little surprised at the downvotes for a working answer (although it was of dubious taste)... – antinome Feb 01 '16 at 13:27
0

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
Mohamad
  • 34,731
  • 32
  • 140
  • 219
0

This evaluates to true for nil and zero: nil.to_s.to_d == 0

Sam
  • 4,000
  • 20
  • 27
0

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 ifs usually end up as cases 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.

0
unless (val || 0).zero?

    # do stufff

end
Abel
  • 3,989
  • 32
  • 31
0

In a single stretch you can do this:

[do_something] if val.to_i == 0

nil.to_i will return 0

-1

Another solution:

if val.to_i == 0
  # do stuff
end
Scott
  • 7,034
  • 4
  • 24
  • 26
-3
val ||= 0
if val == 0
# do something here
end
lmumar
  • 113
  • 6