views:

6965

answers:

15

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.

+3  A: 

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
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
I'd use .nil? not == nil
RichH
+13  A: 

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
Very right about the code smell! +1Might consider looking into NullObject pattern http://en.wikipedia.org/wiki/Null_Object_pattern
abyx
+8  A: 

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
I would modify the Numeric class rather than Object.
epochwolf
epochwolf, if you put this on the Numeric class, then `nil?` would always return false. `nil?` only returns true on the NilClass.
Ryan McGeary
+2  A: 

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
+16  A: 

Objects have a nil? method. See api here.

if (val.nil? or val == 0)
  [do something]
end

Or, for just one instruction:

[do something] if (val.nil? or val == 0)
Christian Lescuyer
Mike Woodhouse
I agree with some poster of the "smell" in the question, however to me this is more the ruby way.
Jonke
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
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.
The Wicked Flea
I'd vote for `val.zero?` instead of `val == 0`
Ryan McGeary
Nakilon
A: 

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
+2  A: 

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
A: 
val ||= 0
if val == 0
# do something here
end
-1: What if the value in `val` needs to be kept? You're clobbering the input data with this method, and there are better, less destructive ways to check for nil or zero.
Platinum Azure
+5  A: 

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.

A: 

I've just removed my previous answer. I misunderstood you and advised to use #blank? method.

Either way, I would extend Object class and create a method for this purpose.

maurycy
A: 

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.

A: 

Another solution:

if val.to_i == 0
  # do stuff
end
Scott
This only works if val is either an integer or nil; 0.5.to_i == 0, any_string.to_i == 0, etc.
Zach Langley
`"5".to_i == 5`, but you're more or less right. Clever but doesn't actually work.
Andrew Grimm
A: 

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
-1 for the same reason as why Scott's answer doesn't work.
Andrew Grimm
A: 

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
A: 

Although to_i doesn't work, to_f does:

if val.to_f.zero?
# do something there
end
Stefan