I tested this on console:
[] || 1 # => []
Shouldn't it return the value that exits, and not []? I can change it to ternary operator, which works fine, but why does the condition above not work?
Because [] is truthy value in Ruby, so the second part of your expression is never executed, it always evaluates to []. In Ruby, just false and nil aren't truthy.
Oh, anyway, you don't need that. map returns an empty array if the array is empty.
Model.new(
name: abc.name,
description: abc.description,
product_ids: abc.product_ids.map(&:id)
)
The exact semantics of || are:
if first expression is not nil or false, return it
if first expression is nil or false, return the second expression
So since [] is truthy it will evaluate to [], as explained by #Ursus.
Related
It might be a dumb question, but can someone please explain this:
if ""
true
else
false
end
=> true (OK)
!!("")
=> true (OK)
"" === true
=> false (OK)
"" == true
=> false (But why?)
Have a nice day!
The basic concept of this question is a deep misunderstanding of Ruby operators. Here's the short of it - there's no such thing as operators in Ruby! All this !, =, == and === that you throw around - these are not operators.
So what's going on?
Ruby is an object oriented language (a real one, not like that fake Java and JavaScript things) and all these fancy character sequences you think of as operators are actually method calls on the object in their left:
== method usually checks for equality - is the object on the right equal in content.
=== method is not used for "strict equality" or "identity" like it is often in other languages - some Ruby objects that implement it use it for "membership" tests, like in ranges ((1..3) === 2 ==> true) or regular expressions (/el/ === "hello" ==> true you can think of regular expressions as the group of all strings that would match), others implement it as equals.
So - how is the if working? Well, if and other "forced boolean contexts" check for "falsehood". In Ruby we recognize two false values - false and nil, everything else will run the truth branch of an if. This is actually the method ! that you also used in your second example. This method is implemented in BasicObject to return true for all objects, except if the object's type is the type of false - FalseClass or the type for nil - NilClass, in which case it returns true.
So your examples actually mean:
Check for the truthiness of "". Because that value's type is neither FalseClass or NilClass, it is always true.
For the value "", call the method ! - which will return false because the object is neither a FalseClass or a NilClass - then call ! on the result, which will return true because false is a FalseClass instance.
For the value "" call the method === with the argument true, but since it is an alias for == - call that instead (see 4).
For the value "" call the method == with the argument true. String's implementation of == will never return true for an argument that isn't a String type.
The only falsy values in Ruby are false and nil. Everything else is "truthy," but not necessarily equal to true. This includes empty objects like a String, Hash, or Array.
Pragmatically, it might help to think of == as "comparably equivalent to" rather than "equals." For example:
1 == 1.0 #=> true
This is true even though one is an Integer and one is a Float because they are comparably equivalent in value, even if they aren't the same object or of the same type.
In the same way, "" is truthy because it is not comparably equivalent to false or nil. However, it's also not the same object type as true, nor comparably equivalent to true. An empty String is simply "not falsy," which makes it truthy but not actually true.
Remember, only false and nil are falsy. Everything else, and I mean everything, is truthy even if it isn't strictly speaking true.
In Ruby, the only "falsey types" are FalseClass and NilClass, which have the instances false and nil respectively. All other values are considered "truthy". This is possibly different to what you'd expect coming from other C-like values, in which we do things like this pretty freely:
int x = get_value();
if (x) { /* implied x != 0 }
So, if you had something like this in Ruby:
puts 0 if 0 # => "0"
puts 1 if "" # => "1"
puts 2 if [] # => "2"
puts 3 if false # => nil
puts 4 if true # => "4"
puts 5 if nil # => "5"
So, if "" acts truthy, why isn't it equal to true? If that were how we defined ==, then this would also need to resolve to true then, since both values are truthy:
"1" == "2"
The difference here is that == is asking if two things are the same, which "" and true are most certainly not. Further, Ruby does not automatically convert types for you (like other languages like JavaScript do), so "" does not automatically get converted to a boolean during its comparison.
I encountered some code that looks like:
(input_array || []).each do |a|
some stuff
end
What is the purpose of input_array || []? I would naively think that this would evaluate to a boolean value which would cause each to throw an error, but that's clearly not whats happening.
The semantics of || are:
if the first expression is not nil or false, return it
if the first expression is nil or false, return the second expression
This is used to provide a default value if the first is nil.
Trying to learn Ruby I ran into this kind of syntax.... Can anyone explain me what it means?
a = nil if b.nonzero?
nonzero? : Returns self if num is not zero, nil otherwise.
And thus does not return a boolean
Values in ruby are truthy and falsey. That is, if a value is not nil, or false, it is true. So if you have a function that returns 1, you can use that in a boolean expression some_function && true would resolve true.
Likewise, if it returned nil, some_function && true would return false.
There's a detailed and in depth explanation here: https://gist.github.com/jfarmer/2647362
I am dealing with someone else's code base, and I am trying to decipher the following line:
if !foo #<~ this line
...
end
I noticed that foo is supposed to return false, but with this bang! method it returns true. Does this mean that this ![var] business is similar to saying:
if foo != nil
Yes, the above code means to run the code if the condition (foo) is false. I won't say its similar to foo != nil, its more like !(foo) where foo can be nil or false.
You can write
if !foo #<~ this line
...
end
as
unless foo
...
end
But as per Ruby style guide, you should use unless, only if the code can be represented in a single line.
do_something unless foo
It means "if not foo", and it effectively returns the opposite of foo. So if foo = true, !foo returns false, and if foo = false, !foo returns true.
!foo
is syntactic sugar for
foo.!
i.e. sending the message ! to the object returned by evaluating the expression foo.
The method ! is defined in BasicObject, and it is documented just like any other method, even if rather tersely:
!obj → true or false
Boolean negate.
In this case the ! negates the value that follows it. In Ruby, only false and nil are considered falsy values, or in other words, values that will be considered false in a conditional statement like if. All other values (true, 1, "some-string", etc...) are considered truthy (values that will be considered true in a conditional).
So !false, is true. !nil is true. !true is false.
Defining a method doesn't seem to evaluate to a truthy value as can be checked by putting one inside an if condition:
if(def some_method; puts "random text"; end) then
puts "declaration evaluates to true"
else
puts "declaration evaluates to false"
end
# => declaration evaluates to false
Why/How does a method declaration evaluate to nil?
It actually evaluates to nil. This makes sense; why would a method creation return anything?
irb(main):001:0> def test; print 'test'; end
=> nil
However, it has to return something, so to return "nothing" would be to return nil.
Every statement in Ruby evaluates to something. The def statement's value is not supposed to be checked and is therefore nil.
You will find the behavior you are looking for in the reflective "meta-programming" method define_method.
class EmptyClass
m = define_method(:newmethod) {p "I am the new method"}
p m # => <Proc:0x50b3f359#E:\NetBeansProjects\RubyApplication1\lib\main.rb:6>
end
From Ruby gotchas:
Boolean evaluation of non-boolean data is strict: 0, "" and [] are all evaluated to true. In C, the expression 0 ? 1 : 0 evaluates to 0 (i.e. false). In Ruby, however, it yields 1, as all numbers evaluate to true; only nil and false evaluate to false. A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, but nil on failure. This convention is also used in Smalltalk, where only the special objects true and false can be used in a boolean expression.
Method definions such as def some_method; puts "random text"; end always return nil.
Now, that means the method is evaluated to nil. According to the Ruby Documentation:
Returns false if obj is nil or false; true otherwise.
Since your method return nil, if will evaluate it as false therefore execute the else statement.