The class below defies my understanding that nil && 'foo' should return nil and not execute 'foo'
no matter what I tried, with or without parenthesis, #user.another_boolean always returns undefined method another_boolean for nil nilclass. I thought if #user is nil it should stop evaluating there and return nil.
class MyClass
def initialize(user, variable = nil)
#user = user
#variable = variable || user.try(:variable)
end
def result
#result ||= !!(#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean)
end
end
I also tried to look for the documentation of the && operator inside the ruby documentation but could only find a reference to and which shouldn't be the same thing given their precedence difference.
Any help much appreciated.
Ruby version: 2.2.5
Edit:
#user and #variable are rails model
Rails version: 4.2
It is standard practice in software for && to have a higher precedence than ||.
So the following are all logically equivalent:
b && a || c
a && b || c
c || b && a
c || a && b
Now, your code is a little longer:
#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
But again we can group the && operators together to show what it's equivalent to:
(#user && #variable && #variable.a_boolean) ||
(#user.another_boolean?) ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
Therefore if #user && #variable && #variable.a_boolean == false, then #user.another_boolean? will be evaluated.
I'm not clear what it is you're trying to achieve - so I don't know if the above logic is correct, or how one might "fix" it, but there's your explanation for why the method is being called.
Your expression has a form of:
aaa &&
bbb &&
bbb.foo ||
aaa.bar ||
aaa.baz && something
it may be reformatted as:
aaa && bbb && bbb.foo
||
aaa.bar
||
aaa.baz && something
It's the same, just whitespaces are laid out differently.
As you can see here, not all terms are protected by the initial aaa&&bbb test.
Most probably you meant this:
#result ||= !!( (
#user &&
#variable
)
&&
(
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean?
)
&& instance_method_retuning_a_boolean
)
I've added way too many parentheses than needed, but this way you exactly see what's going on.
Hi Yann and welcome to Stackoverflow. Let me give you some examples that may help you understand the reason for your observation.
You correctly stated that:
nil && true
=> nil
but if you chain additional operators without explicitly use brackets then the following happens:
nil && true || true
=> true
This is because the && operator has higher precedence so you could write the same thing like this, and then its clear why the expression does not stop after the first nil:
(nil && true) || true
I found this article pretty helpful: https://womanonrails.com/operator-precedence-ruby.
So for your case if we would put the brackets as it is now we would have the following:
(#user && #variable && #variable.a_boolean) ||
#user.another_boolean? ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
This means that even if the first part of the expression results in false, the #user.another_boolean? still gets evaluated.
So what you probably want is putting brackets explicitly:
(#user && #variable) &&
(#variable.a_boolean || #user.another_boolean? || #user.a_third_boolean?) &&
instance_method_retuning_a_boolean
So now you have the first part, which will check if both #user and #variable are not nil. If any of those is nil, the second part will not be evaluated anymore.
Hope this brings some clarity.
You can probably avoid an overly complex boolean expression by adding a guard clause (or two) that separates the prerequisite conditions from the actual result:
def result
return unless #user
return unless #variable
#result ||= #variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
end
I'm not sure if this returns the expected result, but you get the idea.
Is there a simple way of testing that several variables have the same value in ruby?
Something linke this:
if a == b == c == d #does not work
#Do something because a, b, c and d have the same value
end
Of course it is possible to check each variable against a master to see if they are all true, but that is a bit more syntax and is not as clear.
if a == b && a == c && a == d #does work
#we have now tested the same thing, but with more syntax.
end
Another reason why you would need something like this is if you actually do work on each variable before you test.
if array1.sort == array2.sort == array3.sort == array4.sort #does not work
#This is very clear and does not introduce unnecessary variables
end
#VS
tempClutter = array1.sort
if tempClutter == array2.sort && tempClutter == array3.sort && tempClutter == array4.sort #works
#this works, but introduces temporary variables that makes the code more unclear
end
Throw them all into an array and see if there is only one unique item.
if [a,b,c,d].uniq.length == 1
#I solve almost every problem by putting things into arrays
end
As sawa points out in the comments .one? fails if they are all false or nil.
tokland suggested a very nice approach in his comment to a similar question:
module Enumerable
def all_equal?
each_cons(2).all? { |x, y| x == y }
end
end
It's the cleanest way to express this I've seen so far.
How about:
[a,b,c,d] == [b,c,d,a]
Really just:
[a,b,c] == [b,c,d]
will do
a = [1,1,1]
(a & a).size == 1 #=> true
a = [1,1,2]
(a & a).size == 1 #=> false
[b, c, d].all?(&a.method(:==))
I'm currently ripping my hairs off to find a solution. I need to parse some Json but the json gem don't allow me to parse more than 2 levels deep, so I have to do a lot of each block. And to eliminate some datas that I don't want I need to do an if/else.
So here is what I'd like to do:
result['person']['participation'].each do |participation|
if participation['movie']['release'] { |release| release['releaseState']['code'] == 3011 || release['releaseState']['code'] == nil }
next
else
puts title = participation['movie']['originalTitle']
end
end
I tried with 'unless' too, but I fear that I can't pass block in statement. If not, do you have a way to do what I need ?
Many thanks !
Here is the JSON http://api.allocine.fr/rest/v3/filmography?partner=YW5kcm9pZC12M3M&profile=medium&code=5568&filter=movie&format=json
I need to see your document structure... but if participation['movie']['release'] is an Array, you can use Array#any?
result['person']['participation'].each do |participation|
if participation['movie']['release'].any? { |release| release['releaseState']['code'] == 3011 || release['releaseState']['code'] == nil }
next
else
puts title = participation['movie']['originalTitle']
end
end
If it's not an Array, please post a document example.
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 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
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?
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.
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
Short and clear
[0, nil].include?(val)
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.
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:
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.
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 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.
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
In a single stretch you can do this:
[do_something] if val.to_i == 0
nil.to_i will return 0
Another solution:
if val.to_i == 0
# do stuff
end
val ||= 0
if val == 0
# do something here
end