simple ruby unless method mistake - ruby

I am very new to ruby. This might sound very naive to you but I can't understand what is happening here
matched_criteria = match_criteria(c1, c2)
puts "answer is #{matched_criteria}"
def match_criteria(crit_val1, crit_val2)
if crit_val1.present?
puts "present"
else
puts "absent"
end
return true unless crit_val1.present? || crit_val2.present?
return false unless crit_val1.present? && crit_val2.present?
end
Output:
absent
answer is false
please explain

def match_criteria(crit_val1, crit_val2)
# Check if #1 is present? or blank?
if crit_val1.present?
puts "present"
else
puts "absent"
end
# Return true unless EITHER (||) are present
# ...or you could think of it like...
# Return true if BOTH (&&) are blank
return true unless crit_val1.present? || crit_val2.present?
# Return false unless BOTH are present
# ..or...
# Return false if EITHER is blank
return false unless crit_val1.present? && crit_val2.present?
end
This is actually a Ruby on Rails question. blank? and present? are instance methods only available in Rails, but not in the standard Ruby library.

Whenever you call match_criteria with one parameter that is a non-blank value, the condition
crit_val1.present? || crit_val2.present?
will be true, but since you are negating it the first return statement does not get called.
In that same situation the second conditional evaluates to false, and since you're negating it the second return successfully returns false.

Your problem has nothing to do with Ruby (or Rails) per se. It has to do with misunderstanding the logical operators involved, which of course would be a problem in any language.
Consider this:
def test(x, y)
return true unless x || y
false unless x && y
end
p test(true, false) # => false
p test(true, true) # => nil
p test(false, false) # => true
In the first test (x = true, y = false) the first condition fails because x || y is true (it would succeed only if x || y evaluated to false, since you're using unless instead of if). The second condition succeeds because x && y is not true. So, the method returns false.
In the second test, neither condition succeeds, because both x || y and x && y evaluate to true. (Again, since you are using unless instead of if, a condition can only succeed if it evaluates to false.) So, the method returns nil.
In the third test, the first condition succeeds, because x || y evaluates to false. So, the method returns false.
It's most unlikely that you want these logical conditions. In the first place, you probably don't want a set of logical conditions that doesn't cover all the possibilities. Also, I don't see a reason for using unless instead of if; it seems like you're needlessly complicating your logic.
Of course, you haven't explained exactly what logical conditions you're looking for, so we're all playing "blind mechanic" here to some extent. But my feeling is that you are attempting to check whether your crit_val1 or crit_val2 are present, and return false if either of them is. If so, all you would have to do is this:
def match_criteria(crit_val1, crit_val2)
# other stuff
return false if crit_val1.present? || crit_val2.present?
true
end

Related

I need to write a regex in Ruby that returns true / false

I am trying to write a regex that takes a word and returns true for words starting with a vowel and returns false for words starting with a consonant. I have never written regex before, and I'm a little confused on how to write the expression. This is what I have so far:
def starts_with_a_vowel?(word)
if word.match(/\A+[aeiou]/) == true
return true
else
return false
end
end
Edit: So if word = "boat" , expression should return false. If word = "apple", expression should return true.
word.match? /\A[aeiou]/i is literally all you need. (ruby >= 2.4)
It matches the beginning of the string \A followed by a vowel [aeiou] in a case-insensitive manner i returning a bool word.match?
Before ruby 2.4 you have to use word.match and convert it to a bool, which is easiest done by logically negating it twice with !!
EDIT:
OK.. So.. I never tested the code I formerly wrote.. it was meant only as some suggestion how to use the match with regex, but it not worked at all.. (that code snippet is now attached to the end of my answer fyi)
this here should be the working one:
def starts_with_a_vowel?(word)
!!word.capitalize.match(/\A+[AEIOU]/)
end
..but how it was mentioned by Eric Duminil here below in comments, the method/function is
not needed
!!word.capitalize.match(/\A+[AEIOU]/) can be used directly..
it returns true or false
but there are surely other (maybe better) solutions too..
..and here is the NOT working code, which I formerly wrote:
def starts_with_a_vowel?(word)
return word.match(/\A+[aeiou]/).length > 0
end
..the match method returns nil when not match and because nil has no length method defined, it raises NoMethodError
Here are a couple of ways to do this.
#1
word = 'Ahoy'
!!(word[0] =~ /[aeiou]/i)
#=> true
word = 'hiya'
!!(word[0] =~ /[aeiou]/i)
#=> false
The regex reads, "match a vowel, case indifferently (/i)". !! converts a thruthy value to true and a falsy value (nil or false) to false:
!!0 = !(!0) = !(false) = true
!!nil = !(!nil) = !(true) = false
#2
word = 'Ahoy'
(word[0] =~ /[aeiou]/i) ? true : false
#=> true
word = 'hiya'
(word[0] =~ /[aeiou]/i) ? true : false
#=> false
You're doing a lot of extra work for no reason. First, you don't need to check for equality with true; just if *expression* does the trick.
But in this case you don't need if at all. A regex match already returns a value that can be interpreted as a Boolean. =~ returns the index of the match as an integer (which is "truthy"); String#match returns a MatchData object (which is also truthy). Everything in Ruby is truthy except false and nil, and nil is what both =~ and String#match return if there's no match. So all you have to do is turn the result of one of those into the corresponding Boolean value, which you can do with !!. For example:
def starts_with_a_vowel? word
!!(word =~ /^[aeiou]/)
end
That !! is read "not not", by the way. The ! operator by itself treats its argument as Boolean and returns its opposite as an actual Boolean; that is !(some truthy value) returns false (not nil), and !(some falsey value) returns true (not just some truthy value). So applying ! twice turns any truthy value into true and any falsey value (false or nil) into false.
Do you have to use a regular expression? Just asking cause Ruby already provides String#start_with?
vowels = %w(a e i o u)
"boat".start_with?(*vowels)
# => false
"apple".start_with?(*vowels)
#=> true
In Ruby, you almost never need anything to return true or false. For boolean logic and if/case/unless statements, truthy/falsey are good enough. Also, don't forget to use case-insensitive Regex (with //i). A is a vowel :
class String
def starts_with_a_vowel?
self =~ /\A[aeiou]/i
end
end
if "Airplane".starts_with_a_vowel?
puts "Indeed"
end
#=> "Indeed"
If for some reason you really need true/false :
class String
def starts_with_a_vowel?
!(self =~ /\A[aeiou]/i).nil?
end
end

Exclamation points before a variable in Ruby

going through ruby monk and once in a while they throw out a code from the left field with unfamiliar syntaxes:
def compute(xyz)
return nil unless xyz
xyz.map {|a,b| !b.nil? ? a + b : a}
end
Can somebody explain these three uses?
1) The exclamation before the object
2) The additional question mark 3) The colon usage within the lambda
! is a just a not operator.
b.nil? is a method that checks the value for b is nil or not. Returns a boolean.
!b.nil? ? a + b : a is a ternary operation is action. It works like this :
if_this_is_a_true_value ? then_the_result_is_this : else_it_is_this
which is equivalent of saying
if a then b else c end
So relating with above statement if !b.nil? is true answer is a+b else it is a.
Read more here
1)
!, negations - it changes every object except nil and false to false (other objects into true)
2) name? should return false(false and nil) or true value (everything else). In most cases it will be true or false objects.
Methods with with ? at the end suggest that they are predicates. In your example nil? checks if object is nil. In other language you may find something like this: is_nil.
Other examples:
[].empty? # true
3.zero? # false
0.zero? # true
3) The colon in your example is part of a ternary if.
'cond' ? 'true value' : 'false value'
is similar to:
if 'cond'
'true value'
else
'false value'
end
One difference between ?: and if else is precedence:
def foo a
a # just return a
end
foo 2 ? 3 : 4
# => 3
foo if 2 then 3 else 4 end
# error
In the last example Ruby wanted to run function1 if condition but it found function if condition #some garbage, so Ruby raised an error.
The exclamation before the object :
!x means negation of x, if x is true, then !x means false and vice-versa
The additional question mark :
x ? y : z
means that if x is true, then return y, else return z
The colon usage within the lambda
I explained in the 2. above

Why does Ruby expression with double ampersand using a return statement cause a syntax error

def foo
true && return false
end
def bar
true and return false
end
foo method causes a syntax error. bar doesn't. Why?
Assuming I want to evaluate one-liners with a return statement (similar to a certain commonly used shell/bash expression), would this be a recommended way to do it, or is there a more recommended approach?
By operator associativity strength,
true && return false
evaluates to
(true && return) false
of which
true && return
is valid, but the false after it is invalid. You cannot have two statements lined up without anything in between.
Side Note
It is worth noting that and and && are not equivalent.
and is a flow control operator while && is a Boolean operator. What is the difference?
One example of the differences, using and, you can assign values.
value = nil and value / 42
This would fail if you tried it as below
value = nil && value / 42
Original Question 1
Assuming I want to evaluate one-liners with a return statement (similar to a certain > commonly used shell/bash expression), would this be a recommended way to do it, or is there > a more recommended approach?
The way I have always seen this done in Ruby is this:
value if conditional
This will return the value if the conditional is met and nil otherwise. No need for the return statement if this is the last command in the function!
If you are using this for an early exit to a function, I prefer using an unless. For instance...
return unless arguments_are_valid
Original Question 2
foo method causes a syntax error. bar doesn't. Why?
It's a matter of operator precedence. See the example below showing how they are evaluated.
(true && return) false
(true) and (return false)
Because of the && operator precedence, the following line
true && return false
evaluates as
(true && return) false
that does not makes sense. In order to execute your code you need to use
def foo
true && (return false)
end
and doesn't suffer of the same issue because and has lower precedence than &&.
if there is need for shorten statements use
def foo
return false if true
end
def bar
return false if true
end
return is not a function :) therefore it doesn't make sense to say
when true and return is ok send a false

Implicit value of if/unless statement in ruby

I'm new to Ruby, and trying to understand why this works (it does seem to, at least according to "the Master"):
def array_of_fixnums?(array)
true unless array.find { |item| item.class != Fixnum }
end
My concern is where the "false-ness" is coming from when the array contains non-fixnum values. Am I right to assume there is no implicit "else false" in the unless statement? In that case I assume it must be coming from the nil value returned by Enumerable#find. Is that correct?
If so, that seems a bit shaky. Might it be better to return false explicitly, like this?
array.find { |item| item.class != Fixnum } ? false : true
Is there another, better way entirely? Thanks for helping me wrap my head around this, and for any "best practice" suggestions.
Your method is returning nil not because find returns nil, but because if your inline conditional does not pass, the method has no explicit return value. This would be more clear if it were not inline, consider:
def array_of_fixnums?(array)
unless array.find { |item| item.class != Fixnum }
return true
end
# otherwise don't explicitly return (implicit nil)
end
While relying on the falsiness of nil will often work, it is problematic in that it does not follow the principle of least surprise. A ? method should return true or false.
But your method has worse problems. It uses confusing logic (a double negative), and itself relies on the falsiness of nil and the truthiness of not nil, to function. Consider what happens if your method were passed [false]. Oops.
The better way would be something like:
array.all? {|n| n.is_a? Fixnum }
The reasoning is that this method does exactly what it says, plainly.
Returning a boolean explicitly, while not necessarily wrong, is superfluous and often considered bad practice. Rather consider the example, which says, in ruby speak, is every one of the values in this array a Fixnum?. The result of that expression is what the method is after; there's no reason to evaluate it then return true|false.
From the find method doc:
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
Thus, all you need is:
def array_of_fixnums?(array)
array.find { |item| item.class != Fixnum }
end
If anything is found, that will be returned, otherwise nil will be returned, and the method will evaluate do false.
However, as the item return could be an false or nil (if any item in the list is false or nil) I would recommend that you use the .any? method instead.
def array_of_fixnums?(array)
array.any? { |item| item.class != Fixnum }
end
Let's look at why true unless condition works as it does.
I believe x unless condition was provided as an esthetic improvement to x if !condition, but the two are equivalent (and, in my opinion, it is an improvement). To get rid of double-negatives, I'll just consider x if condition.
One of Ruby's basic tenets is that every statement must return a value (namely, the last expression evaluated). Therefore, x if condition must be evaluated as if condition then x else y end. But what is y? If y evaluated as true or false, x if condition would be meaningless when x is a boolean expression. To see why, consider:
w = (x == 5 if z == 6)
If w were true, we could conclude that x = 5 and z = 6, but if w were false, we wouldn't know whether z = 6 and x != 5, or z != 6 and x = 5 or x != 5. Considering that any value of y other than nil is evaluated as true or false, the only reasonable value for y is nil. That way, if w == nil is be available. Of course, this is still a problem (in other situations) when x could possibly be nil.
I would welcome clarification and elaboration. Perhaps there is also a less convoluted way of making this argument.

Is there a simple way to evaluate a value to true/false without using an expression?

Is there a simple way in Ruby to get a true/false value from something without explicitly evaluating it to true or false
e.g. how would one more succinctly express
class User
def completed_initialization?
initialization_completed == 1 ? true : false
end
end
is there some way to do something along the lines of
initialization_completed.true?
There's obviously not much in it but since I'm in the zen garden of Ruby I might as well embrace it
EDIT (I've updated the example)
This question was extremely badly phrased as was very gently pointed out by #Sergio Tulentsev. The original example (below) does of course evaluate directly to a boolean. I'm still struggling to find an example of what I mean however Sergio's double-negative was in fact exactly what I was looking for.
Original example
class User
def top_responder
responses.count > 10 ? true : false
end
end
> operator already returns boolean value. So it can be just
def top_responder
responses.count > 10
end
To convert arbitrary values to booleans, I offer you this little double-negation trick.
t = 'foo'
!!t # => true
t = 1
!!t # => true
t = 0
!!t # => true
t = nil
!!t # => false
The first negation "casts" value to boolean and inverts it. That is, it will return true for nil / false and false for everything else. We need another negation to make it produce "normal" values.

Resources