How to write a Ruby switch statement (case...when) with regex and backreferences?
The references to the latest regex matching groups are always stored in pseudo variables $1
to $9
:
case foo
when /^([0-9][0-9])/
print "the month is #{$1}"
else
print "something else"
end
You can also use the $LAST_MATCH_INFO
pseudo variable to get at the whole MatchData
object. This can be useful when using named captures:
case foo
when /^(?<number>[0-9][0-9])/
print "the month is #{$LAST_MATCH_INFO['number']}"
else
print "something else"
end
How to change case of letters in string using RegEx in Ruby
@sawa Has the simple answer, and you've edited your question with another mechanism. However, to answer two of your questions:
Is there a way to do this within the regex though?
No, Ruby's regex does not support a case-changing feature as some other regex flavors do. You can "prove" this to yourself by reviewing the official Ruby regex docs for 1.9 and 2.0 and searching for the word "case":
- https://github.com/ruby/ruby/blob/ruby_1_9_3/doc/re.rdoc
- https://github.com/ruby/ruby/blob/ruby_2_0_0/doc/re.rdoc
I don't really understand the '\1' '\2' thing. Is that backreferencing? How does that work?
Your use of \1
is a kind of backreference. A backreference can be when you use \1
and such in the search pattern. For example, the regular expression /f(.)\1/
will find the letter f
, followed by any character, followed by that same character (e.g. "foo" or "f!!").
In this case, within a replacement string passed to a method like String#gsub
, the backreference does refer to the previous capture. From the docs:
"If replacement is a String it will be substituted for the matched text. It may contain back-references to the pattern’s capture groups of the form
\d
, whered
is a group number, or\k<n>
, wheren
is a group name. If it is a double-quoted string, both back-references must be preceded by an additional backslash."
In practice, this means:
"hello world".gsub( /([aeiou])/, '_\1_' ) #=> "h_e_ll_o_ w_o_rld"
"hello world".gsub( /([aeiou])/, "_\1_" ) #=> "h_\u0001_ll_\u0001_ w_\u0001_rld"
"hello world".gsub( /([aeiou])/, "_\\1_" ) #=> "h_e_ll_o_ w_o_rld"
Now, you have to understand when code runs. In your original code…
string.gsub!(/([a-z])([A-Z]+ )/, '\1'.upcase)
…what you are doing is calling upcase
on the string '\1'
(which has no effect) and then calling the gsub!
method, passing in a regex and a string as parameters.
Finally, another way to achieve this same goal is with the block form like so:
# Take your pick of which you prefer:
string.gsub!(/([a-z])([A-Z]+ )/){ $1.upcase << $2.downcase }
string.gsub!(/([a-z])([A-Z]+ )/){ [$1.upcase,$2.downcase].join }
string.gsub!(/([a-z])([A-Z]+ )/){ "#{$1.upcase}#{$2.downcase}" }
In the block form of gsub the captured patterns are set to the global variables $1
, $2
, etc. and you can use those to construct the replacement string.
Using regex in Ruby/Capybara case statement throws 'Regex matched 0 arguments' error
Your block is taking a parameter page_name
but your Regex doesn't have a capture group in it to be used as that parameter. You probably want something like
Given /^I am on the (.+) page$/ do |page_name|
so the .+
portion is captured and passed through as page_name
How to write a case statement in Ruby with multiple statements inside a when clause?
Your question seems to be "how can I put all those statements on one line to have fewer lines".
Generally, you can use ";" in ruby to replace the End-of-Lines. Thus:
case selection
when 1; system "clear"; view_all_entries; main_menu
...
Or
case selection
when 1 then system "clear"; view_all_entries; main_menu
...
Using the ";" in any way is very much not ruby-like and not recommended. See below for a much nicer refactoring which strips the duplicated code.
Implementing the 'case' statement in order to match multiple 'when' conditions
Ruby doesn't have any sort of fall-through for case
.
One alternative be a series of if
statements using the ===
method, which is what case
uses internally to compare the items.
has_matched? = false
if '2' === var
has_matched? = true
# stuff
end
if '3' === var
has_matched? = true
# other stuff
end
if something_else === var
has_matched? = true
# presumably some even more exciting stuff
end
if !has_matched?
# more stuff
end
This has two glaring issues.
It isn't very DRY:
has_matched? = true
is littered all over the place.You always need to remember to place
var
on the right-hand-side of===
, because that's whatcase
does behind the scenes.
You could create your own class with a matches?
method that encapsulates this functionality. It could have a constructor that takes the value you'll be matching against (in this case, var
), and it could have an else_do
method that only executes its block if its internal @has_matched?
instance variable is still false.
Edit:
The ===
method can mean anything you want it to mean. Generally, it's a more "forgiving" way to test equivalency between two objects. Here's an example from this this page:
class String
def ===(other_str)
self.strip[0, other_str.length].downcase == other_str.downcase
end
end
class Array
def ===(str)
self.any? {|elem| elem.include?(str)}
end
end
class Fixnum
def ===(str)
self == str.to_i
end
end
Essentially, when Ruby encounters case var
, it will call ===
on the objects against which you are comparing var
.
Ruby regex - using optional named backreferences
Problem
The reason it does not match the second line is because the second instance of hat does not end with a slash, but the first instance does.
Solution
Specify that there is a slash between the first and second match
Regex
(top_.*)/(\1.*$)|(^.*$)
Replacement
hier = \2\3
Example
Regex101 Permalink
More info on the Alternation token
To explain how the |
token works in regex, see the example: abc|def
What this regex means in plain english is:
- Match either the regex below (attempting the next alternative only if this one fails)
- Match the characters
abc
literally
- Match the characters
- Or match the regex below (the entire match attempt fails if this one fails to match)
- Match the characters
def
literally
- Match the characters
Example
Regex: alpha|alphabet
If we had a phrase "I know the alphabet", only the word alpha
would be matched.
However, if we changed the regex to alphabet|alpha
, we would match alphabet
.
So you can see, alternation works in a left-to-right fashion.
Write a better switch-case function?
If you wanted to use a hash (as per your question) you could do:
def readable_status(status)
readable = { "1" => "go", "2" => "stop", "3" => "die" }
readable[status] || "default value"
end
Using a case statement to assign multiple variable in ruby
Your return values in the case statement are not accepted by the ruby engine. I think you want to return an array... using the [] perhaps?
Like this:
def hsv_to_rgb(h, s, v)
if (h == 0) then return 0, 0, 0 end
c = v * s
hp = h / 60.0
x = c * (1 - (hp % 2 - 1).abs)
r, g, b = case hp
when 0..1
[c, x, 0]
when 1..2
[x, c, 0]
when 2..3
[0, c, x]
when 3..4
[0, x, c]
when 4..5
[x, 0, c]
else
[c, 0, x]
end
m = v - c
return r + m, g + m, b + m
end
Related Topics
Sass::Syntaxerror: File to Import Not Found or Unreadable: Compass in Production
Insecure World Writable Dir /Users/Username in Path, Mode 040777 When Running Ruby Commands
Using $ Sudo Bundle Exec ... Raises 'Bundle: Command Not Found' Error
How to Create Symbol (Hash Key) from Association, Using New Ruby (1.9) Hash Syntax
What Do I Need to Do to Get the Blog to Work in Rails 4.2
Show Full Path Name of the Ruby File When It Get Loaded
Ruby Metaprogramming: Dynamic Instance Variable Names
Trouble Comparing Time with Rspec
Unix Commands Work on Server But Not in Ruby Ssh Session
Why Is Rake Db:Migrate:Reset Not Listed in Rake -T
Populating an Association with Children in Factory_Girl
Allow Public Connections to Local Ruby on Rails Development Server
What's the Difference Between These Ruby Namespace Conventions