How can I put RegExp in ruby's case condition? - ruby

something like this:
a = 6
case a
when /\d/ then "it's a number"
end
no luck, it doesn't work

When used with a value on the initializer, all case does is try it with === against each expression. The problem isn't with case, try:
6 === /\d/
All that to say, regexes match against strings only. Try replacing the second line by:
case (a.is_a?(String) ? a : a.to_s)
EDIT: To answer the OP's follow-up in comments, there's a subtlety here.
/\d/ === '6' # => true
'6' === /\d/ # => false
Perhaps unexpectedly to the beginner, String#=== and Regexp#=== have different effects. So, for:
case 'foo'
when String
end
This will call String === 'foo', not 'foo' === String, etc.

It doesn't work because regexes match against a string, whereas 6 is not a string. If you do a = '6', it shall work.

Because regexps match strings. A is a Fixnum.
If you would write a = "6", it would work. Testing if a is a number can be done with a.is_a?(Numeric)

One minor change to make it work:
a = 6
case a.to_s
when /\d/ then "it's a number"
end
The to_s will convert everything to a string. Note that your regex just checks for the existence of a digit anywhere in the string.
It would perhaps be better to do this:
case a
when Numeric then "it's a number"
end

Related

What does 'no implicit conversion of Regexp into String (TypeError)' mean?

class String
def digit?
self.include?(/[0-9]/)
end
end
Test.assert_equals "".digit?, false
Test.assert_equals "7".digit?, true
Test.assert_equals " ".digit?, false
I have been playing around with regular expressions. Can you tell me how I have made an error? I have tried explicitly converting it to a string but it doesn't work nor do I see why I should have to. Could anyone enlighten me? Thank you!
include? expects a string as documented here. It seems like you were looking for match.
Matt's answer is good, but it's worth noting that there's some nuance here. Firstly, with the Regexp you have, you're going to match any string that contains any digit:
class String
def digit?
match(/[0-9]/)
end
end
"foo1bar".digit? # => #<MatchData "1">
If you want to match a single digit you'll want to use anchors—\A and \z for the start and end of the string, respectively—and while we're at it, you might as well use \d instead of [0-9], ergo /\A\d\z/. If you want to match one or more digits, use the + quantifier: /\A\d+\z/.
Finally, match might be overkill here. I suggest using the =~ operator:
class String
def digit?
self =~ /\A\d\z/
end
def digits?
self =~ /\A\d+\z/
end
end
"foo123bar".digit? # => nil
"1".digit? # => 0
"123".digit? # => nil
"foo123bar".digits? # => nil
"1".digits? # => 0
"123".digits? # => 0

Ruby, True/false regex

So I've got an issue where my regex looks like this: /true|false/.
When I check the word falsee I get a true from this regex, is there a way to just limit it to the exact true or false words?
Use this regex:
/^(true|false)$/
It will match the beginning and end of the test string with ^ and $, respectively, so nothing else can be in the string (exact match).
See live example at Regex101.
UPDATE (see #w0lf's comment): The parentheses are to isolate the true|false clause so that they are not grouped incorrectly. (This also puts the true or false match in the first capturing group, but since it seems that you are only matching and not capturing an output, this should not make a difference).
Alternatively, if you simply want to match two values, there are easier ways in Ruby. #SimoneCarletti suggests one. You can also use the basic == or eql? operators. Try running the following script to see that these all work:
values = ["true", "false", "almosttrue", "falsealmost"]
values.each do | value |
puts value
# these three are all equivalent
puts "match with if" if value == "true" || value == "false"
puts "match with equals?" if (value.eql? "true") || (value.eql? "false")
puts "match with regex" if /^(true|false)$/.match value
puts
end
You need to use the ^ and $ anchors:
/^(true|false)$/
Edit: As Cary pointed out in the comments, the pattern above will also match multiline strings that happen to contain a line with true or false. To avoid this, use the \A and \z delimiters that match the beginning and end of string respectively:
/\A(true|false)\z/
Try out
/^(true|false)$/
where ^ is the start of a line and $ the end.
You can use
/^(true|false)$/
or even better
/\A(true|false)\z/
that will match the beginning and end of the string (instead of line). If you only need to match for whose words, it may be more efficient to use a simple array and include?:
%w( true false ).include?(value)

Find the first index of a non-numeric character

Suppose I have a String:
someString = "1374j03d42s23dc"
I want to find the first index of a non-numeric character. In this case, that would be 4. How can I do this with a regex?
(I'm not very good at regex, so it would be great if the answer could explain what is going on)
someString =~ /\D/
# => 4
........
In addition to sawa's solution: You could also use String#index when you like your code to be more readable:
string = '1374j03d42s23dc'
string.index(/\D/)
#=> 4
/\D/ matches any non-digit (list of common regexp metacharacters)
Try:
someString.each_char.with_index { |c,i| puts i if c == someString.scan(/\D+/)[0] }

/ell/ === 'Hello' true in Ruby. Why?

This code:
/ell/ === 'Hello'
evalutes to 'true' in IRB.
I don't understand why this makes sense logically. Integer === 30 makes sense because 30 is a PART OF the Integer class, but in what way is the string 'Hello' a PART OF /ell/? I don't get it.
Semantically you're saying does the regular expression 'ell' match the string 'Hello'. Since 'Hello' contains the substring 'ell', it is true.
The '===' method is described here:
http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-i-3D-3D-3D
You should not use === for anything in ruby except case equality, find the documentation on Regex#===
Following a regular expression literal with the === operator allows you to compare against a String.
/^[a-z]$/ === "HELLO" #=> false
/^[A-Z]$/ === "HELLO" #=> true
The === the case operator, it is primarily used in case statements and should not really be seen by its own.
case my_string
when /ll/ then puts 'the string migth be hello'
when /x/ then puts 'all i know is that the sting contain x'
else puts 'I have no idea'
end
It can also be used in some other functions such as grep:
array = ['ll', 'aa', 'hello']
p array.grep(/ll/){|x| x.upcase} #=> ["LL", "HELLO"]
Any other use is discouraged and it really does not need to make any sense.
A regular expression describes a language, i.e. a set of strings. The === checks whether the string is a member of that set.
See my answer to a similar question for details.

Ruby: How to find out if a character is a letter or a digit?

I just started tinkering with Ruby earlier this week and I've run into something that I don't quite know how to code. I'm converting a scanner that was written in Java into Ruby for a class assignment, and I've gotten down to this section:
if (Character.isLetter(lookAhead))
{
return id();
}
if (Character.isDigit(lookAhead))
{
return number();
}
lookAhead is a single character picked out of the string (moving by one space each time it loops through) and these two methods determine if it is a character or a digit, returning the appropriate token type. I haven't been able to figure out a Ruby equivalent to Character.isLetter() and Character.isDigit().
Use a regular expression that matches letters & digits:
def letter?(lookAhead)
lookAhead.match?(/[[:alpha:]]/)
end
def numeric?(lookAhead)
lookAhead.match?(/[[:digit:]]/)
end
These are called POSIX bracket expressions, and the advantage of them is that unicode characters under the given category will match. For example:
'ñ'.match?(/[A-Za-z]/) #=> false
'ñ'.match?(/\w/) #=> false
'ñ'.match?(/[[:alpha:]]/) #=> true
You can read more in Ruby’s docs for regular expressions.
The simplest way would be to use a Regular Expression:
def numeric?(lookAhead)
lookAhead =~ /[0-9]/
end
def letter?(lookAhead)
lookAhead =~ /[A-Za-z]/
end
Regular expression is an overkill here, it's much more expensive in terms of performance. If you just need a check is character a digit or not there is a simpler way:
def is_digit?(s)
code = s.ord
# 48 is ASCII code of 0
# 57 is ASCII code of 9
48 <= code && code <= 57
end
is_digit?("2")
=> true
is_digit?("0")
=> true
is_digit?("9")
=> true
is_digit?("/")
=> false
is_digit?("d")
=> false

Resources