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
The and and or keywords are banned. It's just not worth it. Always use && and || instead.. And it's right, for David and Tom reasons.nil (not nul)! This is what it's like to be a newbie at programming (for someone who's been writing code for 17 years!).unless with multiple boolean conditions is discouraged by multiple sources (just google a bit...). It's more difficult to reason about what is happening because the logic is reversed. I'd really just keep this with the if clause..zero? is to be preferred over == 0 (same for negated)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.
def real_value; self != 0 ? self : nil; end, maybe even additionally. I couldn't come up with a better name then real_value. And RuboCop tells me that .zero? is to be preferred over == 0 (same for negated)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?
"foo"&.nonzero? # => NoMethodError: undefined method 'nonzero?' for "foo":String .... This is not safe to use on arbitrary objects.nil.nonzero? - if the number was 0". Also the need to check if a completely arbitrary object is 0 arises extremely rarely compared to that to check a number that may or may not be nil. Hence it is almost implied. Even if someone somehow makes the contrary assumption, they will instantly understand what is happening when they try to execute it.value.respond_to?(:nonzero) && value.nonzero? all over my code for that exact reason.&. operator is "conditional send", not "safe navigation". There's a meaningful distinction: blog.jez.io/conditional-sendok, 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.
try method.try to show the alternative option (this is why it was upvoted in the first place!), so long as it's clear to the reader that ActiveSupport is not vanilla ruby.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.
discount.respond_to?(:nonzero) ? discount : discount.nonzero? for robustness. This is also very rubyish as 5.nonzero? # => 5if (discount||0) != 0
#...
end
.zero? is to be preferred over == 0 (same for negated)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.
"".to_i == "foo".to_i == "0".to_i == 0. Your method will make all sorts of unintended type coercions. It will also fail with a NoMethodError if discount does not respond to to_i.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.
blank? is a rails-specific method, and not available in vanilla ruby.blank_or_zero?In modern rails I like using positive? in this context. positive? returns a boolean, but nonzero? returns either nil or a value.
if(discount&.positive?)
...
end
Examples:
With nonzero?
[nil,0, 1].map{|v| v&.nonzero?}
=> [nil, nil, 1]
And with positive?
[nil,0, 1].map{|v| v&.positive?}
=> [nil, false, true]
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
true if discount were nil.
discountis false?discount.in? [0, nil]the cleaner way possible