Ruby Regex Return - ruby

This is my code
pong = /return/.match(cap.array[0])
if pong == "return"
puts "Pong"
end
cap.array[0] is definitely matching /return/ as you can see when I use pp:
#<MatchData "return">
but for some reason, the if statement isn't triggering. Any ideas why it won't match? I tried == and === thinking it could be a type issue, but no joy.

Why two time check?? You can do it in simple way like below:
if /return/ =~ cap.array[0]
puts "pong"
end
For example:
/return/ =~ "Functions return value" #=> 14
/return/ =~ "return" #=> 0
In ruby:
puts "hello" if 0 #=> hello # 0 is not false
So you can avoid multiple checks

Regex#match returns a MatchData object. Thus, its comparison to string fails.
pong = /return/.match('i shall return this')
# cast it to string
if pong.to_s == "return"
# or do this
# if pong[0] == "return"
puts "Pong"
end

Related

Ruby: Logic of regular expression

Player = Struct.new(:reference, :name, :state, :items, :location)
# Setting game initials
game_condition = 0
player = Player.new(:player, "Amr Koritem", :alive, [:knife, :gun])
puts player.name
player.location = :jail5
class Dungeon
attr_accessor :player, :rooms, :prisoners, :gangsmen, :policemen
##counter = 0
def initialize(player)
#player = player
end
end
my_dungeon = Dungeon.new(player)
if my_dungeon.player.location.to_s.scan(/\D+/) == "jail"
puts "yes"
end
This code is supposed to print "yes" on the screen, but it doesn't. I changed the == sign to != and surprisingly it printed "yes" !
I thought may be I understood the regular expression wrong so I typed this code:
puts my_dungeon.player.location.to_s.scan(/\D+/)
It prints "jail" on the screen, which means I wasn't wrong, was I ?
Can anyone explain this please ?
As Wiktor's comment says, arrays are always truthy, and scan always returns an array, even if there are no matches. Instead you can use any of the following methods:
str = "jail5"
if str[/\D+/] # => nil or the match contents
if str.match /\D+/ # => nil or MatchData object
if str =~ /\D+/ # => nil or index of the match
unless str.scan(/\D+/).empty?
if str.scan(/\D+/).length > 0
In general when you come across surprising behavior like this you should do a bit of introspection - check what the result value is using print or a breakpoint.

Ruby code to check for Integer [duplicate]

I need a function, is_an_integer, where
"12".is_an_integer? returns true.
"blah".is_an_integer? returns false.
How can I do this in Ruby? I would write a regex but I'm assuming there is a helper for this that I am not aware of.
Well, here's the easy way:
class String
def is_integer?
self.to_i.to_s == self
end
end
>> "12".is_integer?
=> true
>> "blah".is_integer?
=> false
I don't agree with the solutions that provoke an exception to convert the string - exceptions are not control flow, and you might as well do it the right way. That said, my solution above doesn't deal with non-base-10 integers. So here's the way to do with without resorting to exceptions:
class String
def integer?
[ # In descending order of likeliness:
/^[-+]?[1-9]([0-9]*)?$/, # decimal
/^0[0-7]+$/, # octal
/^0x[0-9A-Fa-f]+$/, # hexadecimal
/^0b[01]+$/ # binary
].each do |match_pattern|
return true if self =~ match_pattern
end
return false
end
end
You can use regular expressions. Here is the function with #janm's suggestions.
class String
def is_i?
!!(self =~ /\A[-+]?[0-9]+\z/)
end
end
An edited version according to comment from #wich:
class String
def is_i?
/\A[-+]?\d+\z/ === self
end
end
In case you only need to check positive numbers
if !/\A\d+\z/.match(string_to_check)
#Is not a positive number
else
#Is all good ..continue
end
You can use Integer(str) and see if it raises:
def is_num?(str)
!!Integer(str)
rescue ArgumentError, TypeError
false
end
It should be pointed out that while this does return true for "01", it does not for "09", simply because 09 would not be a valid integer literal. If that's not the behaviour you want, you can add 10 as a second argument to Integer, so the number is always interpreted as base 10.
Ruby 2.6.0 enables casting to an integer without raising an exception, and will return nil if the cast fails. And since nil mostly behaves like false in Ruby, you can easily check for an integer like so:
if Integer(my_var, exception: false)
# do something if my_var can be cast to an integer
end
"12".match(/^(\d)+$/) # true
"1.2".match(/^(\d)+$/) # false
"dfs2".match(/^(\d)+$/) # false
"13422".match(/^(\d)+$/) # true
You can do a one liner:
str = ...
int = Integer(str) rescue nil
if int
int.times {|i| p i}
end
or even
int = Integer(str) rescue false
Depending on what you are trying to do you can also directly use a begin end block with rescue clause:
begin
str = ...
i = Integer(str)
i.times do |j|
puts j
end
rescue ArgumentError
puts "Not an int, doing something else"
end
class String
def integer?
Integer(self)
return true
rescue ArgumentError
return false
end
end
It isn't prefixed with is_. I find that silly on questionmark methods, I like "04".integer? a lot better than "foo".is_integer?.
It uses the sensible solution by sepp2k, which passes for "01" and such.
Object oriented, yay.
The Best and Simple way is using Float
val = Float "234" rescue nil
Float "234" rescue nil #=> 234.0
Float "abc" rescue nil #=> nil
Float "234abc" rescue nil #=> nil
Float nil rescue nil #=> nil
Float "" rescue nil #=> nil
Integer is also good but it will return 0 for Integer nil
I prefer:
config/initializers/string.rb
class String
def number?
Integer(self).is_a?(Integer)
rescue ArgumentError, TypeError
false
end
end
and then:
[218] pry(main)> "123123123".number?
=> true
[220] pry(main)> "123 123 123".gsub(/ /, '').number?
=> true
[222] pry(main)> "123 123 123".number?
=> false
or check phone number:
"+34 123 456 789 2".gsub(/ /, '').number?
A much simpler way could be
/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true
Personally I like the exception approach although I would make it a little more terse:
class String
def integer?(str)
!!Integer(str) rescue false
end
end
However, as others have already stated, this doesn't work with Octal strings.
This might not be suitable for all cases simplely using:
"12".to_i => 12
"blah".to_i => 0
might also do for some.
If it's a number and not 0 it will return a number. If it returns 0 it's either a string or 0.
def isint(str)
return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
end
Here's my solution:
# /initializers/string.rb
class String
IntegerRegex = /^(\d)+$/
def integer?
!!self.match(IntegerRegex)
end
end
# any_model_or_controller.rb
'12345'.integer? # true
'asd34'.integer? # false
And here's how it works:
/^(\d)+$/is regex expression for finding digits in any string. You can test your regex expressions and results at http://rubular.com/.
We save it in a constant IntegerRegex to avoid unnecessary memory allocation everytime we use it in the method.
integer? is an interrogative method which should return true or false.
match is a method on string which matches the occurrences as per the given regex expression in argument and return the matched values or nil.
!! converts the result of match method into equivalent boolean.
And declaring the method in existing String class is monkey patching, which doesn't change anything in existing String functionalities, but just adds another method named integer? on any String object.
Ruby 2.4 has Regexp#match?: (with a ?)
def integer?(str)
/\A[+-]?\d+\z/.match? str
end
For older Ruby versions, there's Regexp#===. And although direct use of the case equality operator should generally be avoided, it looks very clean here:
def integer?(str)
/\A[+-]?\d+\z/ === str
end
integer? "123" # true
integer? "-123" # true
integer? "+123" # true
integer? "a123" # false
integer? "123b" # false
integer? "1\n2" # false
Expanding on #rado's answer above one could also use a ternary statement to force the return of true or false booleans without the use of double bangs. Granted, the double logical negation version is more terse, but probably harder to read for newcomers (like me).
class String
def is_i?
self =~ /\A[-+]?[0-9]+\z/ ? true : false
end
end
For more generalised cases (including numbers with decimal point),
you can try the following method:
def number?(obj)
obj = obj.to_s unless obj.is_a? String
/\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end
You can test this method in an irb session:
(irb)
>> number?(7)
=> #<MatchData "7" 1:nil>
>> !!number?(7)
=> true
>> number?(-Math::PI)
=> #<MatchData "-3.141592653589793" 1:".141592653589793">
>> !!number?(-Math::PI)
=> true
>> number?('hello world')
=> nil
>> !!number?('hello world')
=> false
For a detailed explanation of the regex involved here, check out this blog article :)
One liner in string.rb
def is_integer?; true if Integer(self) rescue false end
I'm not sure if this was around when this question is asked but
for anyone that stumbles across this post, the simplest way is:
var = "12"
var.is_a?(Integer) # returns false
var.is_a?(String) # returns true
var = 12
var.is_a?(Integer) # returns true
var.is_a?(String) # returns false
.is_a? will work with any object.

Calling method isn't returning string

I created a method to count a substring 'e' in a string passed as an argument. If there isn't a substring 'e' in the string, it should return "There is no \"e\"." I am trying to achieve this:
How many times 'e' is in a string.
If given string doesn't contain any "e", return "There is no "e"."
if given string is empty, return empty string.
if given string is nil, return nil.
This is my code:
def find_e(s)
if !s.include?("e")
"There is no \"e\"."
elsif s.empty?
""
else s.nil?
nil
end
s.count("e").to_s
end
find_e("Bnjamin")
It skips the if statement and it still uses the method count. Why is this?
To achieve what you want you could move your string.count to the else statement in your if, because actually you're making your method return the quantity of e passed in the count method over your string, but what happens inside the if isn't being used:
def find_e(s)
if s.nil?
nil
elsif s.empty?
''
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
p find_e("Bnjamin") # => "There is no \"e\"."
p find_e("Benjamin") # => "1"
p find_e(nil) # => nil
p find_e('') # => ""
And also your validations must be in order, first check nil values, then empty values, and then the rest, if you don't then you'll get some undefined method ___ for nil:NilClass errors.
You might have a hard time using the method you wrote. In the next method, you'll need a new case statement to test if find_e returned nil, an empty string, a string with a number or "no e".
This method would be a bit more consistent:
def count_e(string_or_nil)
count = string_or_nil.to_s.count("e")
if count == 0
"There is no \"e\"."
else
count
end
end
puts count_e("Covfefe")
# 2
puts count_e("Bnjamin")
# There is no "e".
puts count_e("")
# There is no "e".
puts count_e(nil)
# There is no "e".
But really, if there's no e in the input, just returning 0 would be the most logical behaviour.
You need to put your count method in a branch of the if/else statement, or else it will be evaluated last every time. Without an explicit return statement Ruby will return the last statement, so putting the method outside the if/else branch on the last line guarantees it will always be hit. Also, nil can be converted to an empty string by calling #to_s, so you can remove one of your branches by converting s.to_s, calling empty? and returning s
def find_e(s)
if s.to_s.empty?
s
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
If you just return 0 whether you get nil, an empty string, or a string without e, you can make it one line
def find_e(s)
s.to_s.count("e").to_s
end
If it were me I'd probably return an Integer, which can always be converted to a String later. puts and "#{}" will implicitly call to_s for you anway. Then you can use that integer return in your presentation logic.
def count_e(input)
input.to_s.count("e")
end
def check_for_e(input)
count = count_e(input)
count > 0 ? count.to_s : "There's no \"e\"."
end
check_for_e("Covfefe") # => "2"
check_for_e("Bnjamin") # => "There's no \"e\"."
check_for_e(nil) # => "There's no \"e\"."
check_for_e("") # => "There's no \"e\"."
In Ruby, methods return the last statement in their body. Your method's last statement is always s.count("e").to_s, since that lies outside of the if statements.

Ruby way to check if a string is not blank?

What's the best way to check if a variable is not blank in an else if condition in Ruby (not Rails)?
elsif not variable.to_s.empty?
# do something
end
or
elsif !variable.to_s.empty?
# do something
end
or
elsif variable.to_s.length > 0
# do something
end
string = ""
unless string.to_s.strip.empty?
# ...
end
I just found out that ''.empty? returns true but ' '.empty? returns false. Even to_s.length for ' ' is not zero.
Maybe it is better to use strip as ' '.strip.empty?
You can use either
unless var.empty?
#do sth
end
or
unless var == ""
#do sth
end
or all of these with if and a negator !.
The source of the empty? method is analogous to the following:
def empty?
return length == 0
end
So, you can safely use
any_string.length != 0
Anyway, using that code inside an else if is a bit verbose, I would encourage you to define the present? method inside the String class.
class String
def present?
!empty?
end
end
Now you can write your code the following way:
if some_condition
# do something
elsif variable.to_s.present?
# do something else
end
This way you get a clear code, without using negations or unless who are hard to read.
Of course, there is one problem here, I took the present? name (and method) from Rails. present? returns true if the object is not blank, but strings with tabs or spaces (white characters) are considered blanks. So, this present? will return true to for the following strings:
"".present? # => false
" ".present? # => true
"\t\n\r".present? # => true
" blah ".present? # => true
It depends on what you want, high chances are that you want to get true for the first 3 strings, and false for the later. You could use #RamanSM approach and use strip to avoid empty spaces
class String
def present?
!strip.empty?
end
end
now, present? returns false for strings with white spaces
"".present? # => false
" ".present? # => false
"\t\n\r".present? # => false
" blah ".present? # => true
Note: Consider that String.present? is present in the ActiveSupport library (which ships with rails) if you add ActiveSupport or use Rails you should use ActiveSupport implementation instead.
If you prefer if to unless...
If you know your variable will be a String...if str[0]
With nil check...if str && str[0] OR if str&.[](0) (I prefer the latter but it might look odd to some people and requires Ruby >= 2.3).
Also...I'd be very careful about calling #to_s on anything because you could end up with unexpected results. If str turns out to be something that you weren't expecting...
str = false
str.to_s[0] # => 'f' (i.e. truthy)
str.to_s.empty? # => false
str = nil
str.to_s[0] # => nil (i.e. falsey)
str.to_s.empty? # => true
I think this caution applies to usage of #to_s in the other answer here as well. Exceptions can be your friend.
For the string (say abc) which is not defined/undefined we should check for abc.nil?
otherwise abc.blank? will throw (NoMethodError) undefined method empty? for nil:NilClass error

Ruby case statement on a hash?

This is going to sound weird, but I would love to do something like this:
case cool_hash
when cool_hash[:target] == "bullseye" then do_something_awesome
when cool_hash[:target] == "2 pointer" then do_something_less_awesome
when cool_hash[:crazy_option] == true then unleash_the_crazy_stuff
else raise "Hell"
end
Ideally, I wouldn't even need to reference the has again since it's what the case statement is about. If I only wanted to use one option then I would "case cool_hash[:that_option]", but I'd like to use any number of options. Also, I know case statements in Ruby only evaluate the first true conditional block, is there a way to override this to evaluate every block that's true unless there is a break?
You could also use a lambda:
case cool_hash
when -> (h) { h[:key] == 'something' }
puts 'something'
else
puts 'something else'
end
Your code is very close to being valid ruby code. Just remove the variable name on the first line, changing it to be:
case
However, there is no way to override the case statement to evaluate multiple blocks. I think what you want is to use if statements. Instead of a break, you use return to jump out of the method.
def do_stuff(cool_hash)
did_stuff = false
if cool_hash[:target] == "bullseye"
do_something_awesome
did_stuff = true
end
if cool_hash[:target] == "2 pointer"
do_something_less_awesome
return # for example
end
if cool_hash[:crazy_option] == true
unleash_the_crazy_stuff
did_stuff = true
end
raise "hell" unless did_stuff
end
I think, following is the better way to do the stuff you want.
def do_awesome_stuff(cool_hash)
case cool_hash[:target]
when "bullseye"
do_something_awesome
when "2 pointer"
do_something_less_awesome
else
if cool_hash[:crazy_option]
unleash_the_crazy_stuff
else
raise "Hell"
end
end
end
Even in case's else part you can use 'case cool_hash[:crazy_option]' instead of 'if' if there are more conditions. I prefer you to use 'if' in this case because there is only one condition.
in ruby 3.0 you can do the following with pattern matching
# assuming you have these methods, ruby 3 syntax
def do_something_awesome = "something awesome 😎"
def do_something_less_awesome = "something LESS awesome"
def unleash_the_crazy_stuff = "UNLEASH the crazy stuff 🤪"
you can do
def do_the_thing(cool_hash)
case cool_hash
in target: "bullseye" then do_something_awesome
in target: "2 pointer" then do_something_less_awesome
in crazy_option: true then unleash_the_crazy_stuff
else raise "Hell"
end
end
will return
do_the_thing(target: "bullseye")
=> "something awesome 😎"
do_the_thing(target: "2 pointer")
=> "something LESS awesome"
do_the_thing(crazy_option: true)
=> "UNLEASH the crazy stuff 🤪"
in ruby 2.7 it still works
# need to define the methods differently
def do_something_awesome; "something awesome 😎"; end
def do_something_less_awesome; "something LESS awesome"; end
def unleash_the_crazy_stuff; "UNLEASH the crazy stuff 🤪"; end
# and when calling the code above to do the switch statement
# you will get the following warning
warning: Pattern matching is experimental, and the behavior may change
in future versions of Ruby!

Resources