How "=~" operator works inside the "IF" block in Ruby? - ruby

Hi find the below two blocks:
Block-I
irb(main):001:0> s="acbbdd"
=> "acbbdd"
irb(main):002:0> /e/=~s
=> nil
irb(main):003:0> if /e/=~s then
irb(main):004:1* print "h"
irb(main):005:1> end
=> nil
Block-II
irb(main):001:0> s="acbbdd"
=> "acbbdd"
irb(main):006:0> if /c/=~s then
irb(main):007:1* print "h"
irb(main):008:1> end
h=> nil
irb(main):009:0>
Could you please help me to understand how =~ works in I and II block? In the first block it doesn't match and returns nil but in the second block how nil is coming?

In the first block, the string doesn't match /e/ - there's no e in "acbbdd".
In the second block, the string does match /c/ - there is a c in "acbbdd".
The reason the nil appears is because it is the return value of print, and hence of the entire if block.

In II , /c/ matches s, so the print "h" get executed. You get an output (the string "h") and a return value from the print statement (nil)
Then the if block returns the return value of the last statement in the block, in this case nil.
run print "h" along will give you same result.

Related

Ruby: return the first character from string if it matches and if there is no match then return nil?

is there a built in way to get first match or nil for a character from string.
i can get a first character like this
'word'.match('o').to_s
but if there is no match it gives "" not nil
Use String#[match_str]:
If a match_str is given, that string is returned if it occurs in the string.
Returns nil if (...) the match string cannot be found.
'word'['o'] #=> 'o'
'word'['q'] #=> nil
The [] method is very versatile, you can also pass a regular expression or indices in different ways.
You can do it in such way:
'word'.scan('o').first #=> "o"
'word'.scan('e').first #=> nil
'word'.match(?o)[0] rescue nil
#=> "o"
'word'.match(?q)[0] rescue nil
#=> nil
'word'.match(/(o)/) && $1
#⇒ "o"
'word'.match(/(s)/) && $1
#⇒ nil

ruby's =~ operator return values?

def starts_with_consonant?(s)
if /^(a|e|i|o|u).*/i =~ s
true
else
false
end
end
# prints out true
puts starts_with_consonant?('aa')
# prints out false
puts starts_with_consonant?('da')
If I change the code just to
def starts_with_consonant?(s)
/^(a|e|i|o|u).*/i =~ s
end
Is that same functionality because
puts starts_with_consonant?('aa').inspect
prints out 0 (Shouldn't it be 1?)
puts starts_with_consonant?('da').inspect
prints out nil
# both print out 0
puts starts_with_consonant?('aa').to_i
puts starts_with_consonant?('da').to_i
What gives?
The =~ operator returns the first match index if the String and Regexp match, otherwise nil is returned:
'foo' =~ /bar/ # => nil
'foo bar' =~ /bar/ # => 4
Your first method, with the if/else statement, is treating the result of the =~ check as "truthy value or not?". If the match is found in the string, it returns the index (in your case, 0) or if it is not found, it returns nil.
0 is a truthy value; nil is not.
Therefore, even though it's returning the same result in each of your methods containing the /.../ =~ s expression, you get different return values out of the methods, depending on what you do with that result.
In the if/else statement, you get true when it's the truthy value of 0, and false when it's the non-truthy value of nil.
In the bare return statement, you get the plain return values of 0 and nil.
well the #=~ method actually returns the index where the first match occurs.
You can't do nil.to_i because that produces zero.
[6] pry(main)> nil.to_i
=> 0
puts starts_with_consonant?('aa').inspect
prints out 0 (Shouldn't it be 1?)
No, it should be 0. Strings are zero-indexed, the pattern has been found on the zeroth position. 0 is a truthy value, triggering the if clause if evaluated there. 'da' =~ /a/ would return 1, since a is the 1st character in the string (d being 0th).
puts starts_with_consonant?('da').inspect
prints out nil
There is no position that matches the pattern, so the return value is nil, a falsy value, which triggers the else clause if evaluated as an if condition.
# both print out 0
puts starts_with_consonant?('aa').to_i
puts starts_with_consonant?('da').to_i
Because both 0.to_i and nil.to_i result in 0.
Your getting back truthy. You can't print it but you can use it, e.g.
2.0.0-p247 :007 > if "aaaabcd" =~ /a/ then puts "true" end
true
=> nil
2.0.0-p247 :008 > if "aaaabcd" =~ /aaa/ then puts "true" end
true
=> nil
2.0.0-p247 :009 > if "aaaabcd" =~ /z/ then puts "true" end
=> nil
Similarly you can set a variable based on the evaluation, i.e.
2.0.0-p247 :013 > if "aaaabcd" =~ /a/ then b=1 end
=> 1
2.0.0-p247 :014 > if "aaaabcd" =~ /aaa/ then b=1 end
=> 1
2.0.0-p247 :015 > if "aaaabcd" =~ /zzz/ then b=1 end
=> nil

Function to strip!, then convert zero-length string to nil

What's the most-efficient manner to remove beginning and ending spaces around a string, then convert the string to nil if the resulting value is zero-length?
For example:
> a=''
> squash(a)
=> nil
> a=' '
> squash(a)
=> nil
> a=' xyz '
> squash(a)
=> 'xyz'
> a=nil
> squash(a)
=> nil
Thus far:
def squash(value)
return nil if value.nil?
value.strip!
(value.blank? ? nil : value)
end
Seems like there could be a more-terse way of implementing this.
** edit **
While I am working in Rails, it would be nice if the answer would contain a Ruby-only implementation, too.
I should emphasize that the implementation needs to be able to handle a string with a nil value.
Assuming you want this for rails (otherwise blank? is undefined) you can use presence method:
def squash(value)
value && value.strip.presence
end
In pure ruby, I would do:
def squash(value)
return unless value
value = value.strip
value unless value.empty?
end
This will work with plain Ruby:
def squash(str)
str = str.to_s.strip
str unless str.empty?
end
Here's one way:
def squash(str)
(str && str[/\S/]) ? str.strip : nil
end
/\S/ looks for a character that is not whitespace.
squash " My dog has fleas. " #=> "My dog has fleas."
squash " " #=> nil
squash nil #=> nil
Reader challenge
I tried to also implement squash!, that would convert the argument str in place. If str is nil, just leave it alone. If str contains a least one non-whitespace character, then str.strip!. However, I could not figure out a way to convert a string to nil. I wanted to do this when the string is empty or contains only whitespace, but the problem is to convert any string, or more generally, any non-nil object, to nil, when the object is received as a method argument. Can it be done? [Edit: #Stefan says the type cannot be changed. I'm sure he's right, but I would like to see where that is written and understand why it is not permitted. Anyone? tidE].
This handles all your examples.
def squash(value)
value.to_s.strip.empty? ? nil : value.strip
end
Just adding this because it's short:
def squash(str)
str.to_s[/\S(.*\S)?/]
end
squash(nil) #=> nil
squash("") #=> nil
squash(" ") #=> nil
squash("a") #=> "a"
squash(" a") #=> "a"
squash("a ") #=> "a"
squash(" a ") #=> "a"
squash(" foo ") #=> "foo"
squash(" foo bar ") #=> "foo bar"
Here's a plain ruby version:
def squash(str)
str && str.strip! && (str unless str.empty?)
end
Update - If you want a version without side effects:
def squash(str)
str && (x = str.strip) && (x unless x.empty?)
end
If you want the method name squash with argument value.
def squash(value)
return value unless value.instance_of?(String)
return if value.strip!&.empty?
value
end
Features:
Works either on pure Ruby or Ruby on Rails
Works with other data types than string as well, for example, you can pass a number if you want
Testing:
squash('ok')
#=> ok
squash('')
#=> nil
squash(' ')
#=> nil
squash(' xyz ')
#=> 'xyz'
squash('xyz ')
#=> 'xyz'
squash(' xyz')
#=> 'xyz'
squash(123)
#=> 123
squash(nil)
#=> nil
Note:
I use safe navigation operator, which was released in Ruby 2.3.0. So make sure before using it.
irb(main):001:0> s = " string "
=> " string "
irb(main):002:0> s.strip!
=> "string"
irb(main):003:0> s.blank?
NoMethodError: undefined method `blank?' for "string":String
from (irb):3
from C:/RUBY/BIN/irb:12:in `'
irb(main):004:0>
I think blank is not Ruby but Rails? Anyway, what's wrong with
(value.length == 0 ? nil : value)
or even better
value.empty? ? nil : value
At least everybody would understand what the intention is here.

find first element in array for which block returns true and return the block's return value

I need to iterate over an array and apply a supplied block to each element, and return the first true value returned by the block, which implies that I need to stop immediately as soon as I get a true value.
below is my code. I am a ruby newbie, and I am not sure if this code is reinventing the wheel. Maybe there is a library method or methods that can do that already? or may be this code can be simplified?
RS = {
:x => %w(\d+ a\d+ bb\d+ ccc\d+).map{|x| /^#{x}$/},
:y => %w(\w+ 1\w+ 22\w+ 333\w+).map{|x| /^#{x}$/}
}.freeze
def find s, t
r = RS[s]
if r
r.each do |p|
m = p.match t
return m if m
end
nil
end
end
p find :x, 'bb12345'
If you want the result of the block you could do it this way. This will iterate over the whole array, but wont evaluate any matches after the first one.
def find(s,t)
RS[s].inject(nil) {|m, p| m || p.match(t)}
end
You can break out early doing something like this
RS[s].inject(nil) {|m, p| (m && (break m)) || p.match(t)}
This is duplicated with: Ruby - Array.find, but return the value the block
You want a lazy map:
[nil, 1, 2, 3].lazy.map{|i| i && i.to_s}.find{|i| i}
# => "1"
Hopefully still actual: here a solution using detect, i made it possible to verbose the output so you can see which expressions are evaluated before returning a hit.
def find_match symbol, string , verbose = false, match = nil
if verbose
RS.detect{|x,v|x==symbol;v.detect{|re|puts re;match=string.match(/#{re}/)}}
else
RS.detect{|x,v|x==symbol;v.detect{|re|match=string.match(/#{re}/)}}
end
match
end
p find_match :x, 'bb12345'
p find_match :x, 'ee12345' , true #verbose output
p find_match :x, '12345'
p find_match :y, '22abcd'
#<MatchData "bb12345">
(?-mix:^\d+$)
(?-mix:^a\d+$)
(?-mix:^bb\d+$)
(?-mix:^ccc\d+$)
(?-mix:^\w+$)
#<MatchData "ee12345">
#<MatchData "12345">
#<MatchData "22abcd">
If your regex patterns are simple, you can just apply the regex again at the end, maybe.
Something like:
def find(s,t)
r = RS[s] and r.find{|p| p.match(t)}.try(:match, t)
end
Although it makes one redundant call to match, it is easier to understand.
First, find the pattern you want, then use that pattern.

What is the difference between print and puts?

For example in this line of code I wrote, print and puts produce different results.
1.upto(1000).each { |i| print i if i % 2 == 0 }
puts adds a new line to the end of each argument if there is not one already.
print does not add a new line.
For example:
puts [[1,2,3], [4,5,nil]] Would return:
1
2
3
4
5
Whereas print [[1,2,3], [4,5,nil]]
would return:
[[1,2,3], [4,5,nil]]
Notice how puts does not output the nil value whereas print does.
A big difference is if you are displaying arrays.
Especially ones with NIL.
For example:
print [nil, 1, 2]
gives
[nil, 1, 2]
but
puts [nil, 1, 2]
gives
1
2
Note, no appearing nil item (just a blank line) and each item on a different line.
print outputs each argument, followed by $,, to $stdout, followed by $\. It is equivalent to args.join($,) + $\
puts sets both $, and $\ to "\n" and then does the same thing as print. The key difference being that each argument is a new line with puts.
You can require 'english' to access those global variables with user-friendly names.
The API docs give some good hints:
print() → nil
print(obj, ...) → nil
Writes the given object(s) to ios. Returns nil.
The stream must be opened for writing. Each given object that isn't a
string will be converted by calling its to_s method. When
called without arguments, prints the contents of $_.
If the output field separator ($,) is not nil, it
is inserted between objects. If the output record separator
($\) is not nil, it is appended to the output.
...
puts(obj, ...) → nil
Writes the given object(s) to ios. Writes a newline after any that
do not already end with a newline sequence. Returns nil.
The stream must be opened for writing. If called with an array argument,
writes each element on a new line. Each given object that isn't a
string or array will be converted by calling its to_s method.
If called without arguments, outputs a single newline.
Experimenting a little with the points given above, the differences seem to be:
Called with multiple arguments, print separates them by the 'output field separator' $, (which defaults to nothing) while puts separates them by newlines. puts also puts a newline after the final argument, while print does not.
2.1.3 :001 > print 'hello', 'world'
helloworld => nil
2.1.3 :002 > puts 'hello', 'world'
hello
world
=> nil
2.1.3 :003 > $, = 'fanodd'
=> "fanodd"
2.1.3 :004 > print 'hello', 'world'
hellofanoddworld => nil
2.1.3 :005 > puts 'hello', 'world'
hello
world
=> nil
puts automatically unpacks arrays, while print does not:
2.1.3 :001 > print [1, [2, 3]], [4]
[1, [2, 3]][4] => nil
2.1.3 :002 > puts [1, [2, 3]], [4]
1
2
3
4
=> nil
print with no arguments prints $_ (the last thing read by gets), while puts prints a newline:
2.1.3 :001 > gets
hello world
=> "hello world\n"
2.1.3 :002 > puts
=> nil
2.1.3 :003 > print
hello world
=> nil
print writes the output record separator $\ after whatever it prints, while puts ignores this variable:
mark#lunchbox:~$ irb
2.1.3 :001 > $\ = 'MOOOOOOO!'
=> "MOOOOOOO!"
2.1.3 :002 > puts "Oink! Baa! Cluck! "
Oink! Baa! Cluck!
=> nil
2.1.3 :003 > print "Oink! Baa! Cluck! "
Oink! Baa! Cluck! MOOOOOOO! => nil
puts call the to_s of each argument and adds a new line to each string, if it does not end with new line.
print just output each argument by calling their to_s.
for example:
puts "one two":
one two
{new line}
puts "one two\n":
one two
{new line} #puts will not add a new line to the result, since the string ends with a new line
print "one two":
one two
print "one two\n":
one two
{new line}
And there is another way to output: p
For each object, directly writes obj.inspect followed by a newline to the program’s standard output.
It is helpful to output debugging message.
p "aa\n\t": aa\n\t
If you would like to output array within string using puts, you will get the same result as if you were using print:
puts "#{[0, 1, nil]}":
[0, 1, nil]
But if not withing a quoted string then yes. The only difference is between new line when we use puts.

Resources