Using Named Captures with regex match in Ruby's case...when?
named captures set local variables when this syntax.
regex-literal =~ string
Dosen't set in other syntax. # See rdoc(re.c)
regex-variable =~ string
string =~ regex
regex.match(string)
case string
when regex
else
end
I like named captures too, but I don't like this behavior.
Now, we have to use $~ in case syntax.
case string
when /(?<name>.)/
$~[:name]
else
end
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
Regex with named capture groups getting all matches in Ruby
Named captures are suitable only for one matching result.
Ruby's analogue of findall
is String#scan
. You can either use scan
result as an array, or pass a block to it:
irb> s = "123--abc,123--abc,123--abc"
=> "123--abc,123--abc,123--abc"
irb> s.scan(/(\d*)--([a-z]*)/)
=> [["123", "abc"], ["123", "abc"], ["123", "abc"]]
irb> s.scan(/(\d*)--([a-z]*)/) do |number, chars|
irb* p [number,chars]
irb> end
["123", "abc"]
["123", "abc"]
["123", "abc"]
=> "123--abc,123--abc,123--abc"
Ruby Named Capture
style, price, stock = <<~_.scan(/^(?:Style |Price \$|Stock \# )(.+)/).flatten
Style 130690 113
Price $335.00
Stock # 932811
_
# => "130690 113", "335.00", "932811"
Ruby regular expression matching enumerator with named capture support
Very identical to the answer you have already seen, but slightly different.
str = "Sun rises at 6:23 am & sets at 5:45 pm; Moon comes up by 7:20 pm ..."
str.gsub(/(?<time>\d:\d{2}) (?<meridiem>am|pm)/).map{ Regexp.last_match }
#=> [#<MatchData "6:23 am" time:"6:23" meridiem:"am">, #<MatchData "5:45 pm" ...
How to do named capture in ruby
You should use match
with named captures, not scan
m = "555-333-7777".match(/(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/)
m # => #<MatchData "555-333-7777" area:"555" city:"333" number:"7777">
m[:area] # => "555"
m[:city] # => "333"
If you want an actual hash, you can use something like this:
m.names.zip(m.captures).to_h # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
Or this (ruby 2.4 or later)
m.named_captures # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
Ruby - best way to extract regex capture groups?
Since v2.4.6, Ruby has had named_captures
, which can be used like this. Just add the ?<some_name>
syntax inside a capture group.
/(\w)(\w)/.match("ab").captures # => ["a", "b"]
/(\w)(\w)/.match("ab").named_captures # => {}
/(?<some_name>\w)(\w)/.match("ab").captures # => ["a"]
/(?<some_name>\w)(\w)/.match("ab").named_captures # => {"some_name"=>"a"}
Even more relevant, you can reference a named capture by name!
result = /(?<some_name>\w)(\w)/.match("ab")
result["some_name"] # => "a"
Why does capturing named groups in Ruby result in undefined local variable or method errors?
Named Captures Must Use Literals
You are encountering some limitations of Ruby's regular expression library. The Regexp#=~ method limits named captures as follows:
- The assignment does not occur if the regexp is not a literal.
- A regexp interpolation,
#{}
, also disables the assignment. - The assignment does not occur if the regexp is placed on the right hand side.
You'll need to decide whether you want named captures or interpolation in your regular expressions. You currently cannot have both.
Ruby regex - gsub only captured group
You can't. gsub
replaces the entire match; it does not do anything with the captured groups. It will not make any difference whether the groups are captured or not.
In order to achieve the result, you need to use lookbehind and lookahead.
"5,214".gsub(/(?<=\d),(?=\d)/, '.')
Ruby Regex: How to match (named) groups inside square brackets?
Square brackets are used for "exactly one of any of these characters" -- that's not what you need here. Pattern-level alternation is done via the |
operator: (hello|goodbye) world
will match either hello world
or goodbye world
.
(?<offset>Z|[+-]\d{2}:\d{2})?
Specifically to parse a datetime, though, I suggest preferring DateTime.parse
(plus to_time
, if you need a Time instance). And if that isn't sufficiently flexible, consider the chronic gem.
Related Topics
Simple Ruby Input Validation Library
Sudo Gem Install Pg Won't Work
In Ruby, Should I Use ||= or If Defined? for Memoization
Deleting While Iterating in Ruby
Counting Days Excluding Weekends
Clarification on the Ruby << Operator
Ruby: How to "Require" a File from the Current Working Dir
How to Get the Version from a Gemspec File
Changing Active Model Serializers Default Adapter
How to Recursively Require All Files in a Directory in Ruby