How to pass a regular expression as an argument to a function? - ruby

The following line of code correctly selects "Joe Bloggs" from a dropdown:
browser.select_list(:id => "ListOwnerID").option(:text => /Joe Bloggs/).select # edited: added '.select'
How do I pass the owner's name as the variable "list_owner"?
Something like:
def set_list_owner(list_owner)
browser.select_list(:id => "ListOwnerID").option(:text => /list_owner/).select
end
Usage:
set_list_owner("Joe Bloggs")

You can use Regexp::new:
re_string = '\d'
Regexp.new(re_string) =~ 'abc 123'
# => 4
Alternative Cary Swoveland suggested (regular expression interpolation):
/#{re_string}/
def set_list_owner(list_owner)
browser.select_list(:id => "ListOwnerID").option(:text => Regexp.new(list_owner))
end
set_list_owner("Joe Bloggs")
If you want to match the string literally, instead of interpreting as a regular expression, use Regexp::escape:
Regexp.new(Regexp.escape(list_owner))

Related

how to remove backslash from a string containing an array in ruby

I have a string like this
a="[\"6000208900\",\"600020890225\",\"600900231930\"]"
#expected result [6000208900,600020890225,600900231930]
I am trying to remove the backslash from the string.
a.gsub!(/^\"|\"?$/, '')
Inside the double quoted string(""), another double quotes must be escaped by \. You can't remove it.
Use puts, you can see it is not there.
a = "[\"6000208902912790\"]"
puts a # => ["6000208902912790"]
Or use JSON
irb(main):001:0> require 'json'
=> true
irb(main):002:0> a = "[\"6000208902912790\"]"
=> "[\"6000208902912790\"]"
irb(main):003:0> b = JSON.parse a
=> ["6000208902912790"]
irb(main):004:0> b
=> ["6000208902912790"]
irb(main):005:0> b.to_s
=> "[\"6000208902912790\"]"
update (as per the last edit of OP)
irb(main):002:0> a = "[\"6000208900\",\"600020890225\",\"600900231930\"]"
=> "[\"6000208900\",\"600020890225\",\"600900231930\"]"
irb(main):006:0> a.scan(/\d+/).map(&:to_i)
=> [6000208900, 600020890225, 600900231930]
irb(main):007:0>
The code a.gsub!(/^\"|\"?$/, '') can't remove the double quote characters because they are not at the beginning and the end of the string. To get what you want try this:
a.gsub(/((?<=^\[)")|("(?=\]$))/, '')
try this:
=> a = "[\"6000208902912790\"]"
=> a.chars.select{ |x| x =~ %r|\d| }.join
=> "6000208902912790"
=> [a.chars.select { |x| x =~ %r|\d| }.join]
=> ["6000208902912790"] # <= array with string
=> [a.chars.select { |x| x =~ %r|\d| }.join].to_s
=> "[\"6000208902912790\"]" # <= come back :)
a="["6000208902912790"]" will return `unexpected tINTEGER`error;
so a="[\"6000208902912790\"]"is used with \ character for double quotes.
As a solution you should try to remove double quotes that will solve the problem.
Do this
a.gsub!(/"/, '')

How to do named capture in ruby

I want to name the capture of string that I get from scan. How to do it?
"555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten #=> ["555", "333", "7777"]
Is it possible to turn it into like this
{:area => "555", :city => "333", :local => "7777" }
or
[["555","area"], [...]]
I tried
"555-333-7777".scan(/((?<area>)\d{3})-(\d{3})-(\d{4})/).flatten
but it returns
[]
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"}
Something like this?
"555-333-7777" =~ /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
Hash[$~.names.collect{|x| [x.to_sym, $~[x]]}]
=> {:area=>"555", :city=>"333", :local=>"7777"}
Bonus version:
Hash[[:area, :city, :local].zip("555-333-7777".split("-"))]
=> {:area=>"555", :city=>"333", :local=>"7777"}
In case you don't really need the hash, but just local variables:
if /(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/ =~ "555-333-7777"
puts area
puts city
puts number
end
How does it work?
You need to use =~ regex operator.
The regex (sadly) needs to be on the left. It doesn't work if you use string =~ regex.
Otherwise it is the same syntax ?<var> as with named_captures.
It is supported in Ruby 1.9.3!
Official documentation:
When named capture groups are used with a literal regexp on the
left-hand side of an expression and the =~ operator, the captured text
is also assigned to local variables with corresponding names.
A way to turn capture group names and their values into a hash is to use a regex with named captures using (?<capture_name> and then access the %~ global "last match" variable.
regex_with_named_capture_groups = %r'(?<area>\d{3})-(?<city>\d{3})-(?<local>\d{4})'
"555-333-7777"[regex_with_named_capture_groups]
match_hash = $~.names.inject({}){|mem, capture| mem[capture] = $~[capture]; mem}
# => {"area"=>"555", "city"=>"333", "local"=>"7777"}
# If ActiveSupport is available
match_hash.symbolize_keys!
# => {area: "555", city: "333", local: "7777"}
This alternative also works:
regex = /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
m = "555-333-7777".match regex
m.named_captures
=> {"area"=>"555", "city"=>"333", "local"=>"7777"}
There are a LOT of ways to create named captures, many of which have been mentioned already. For the record though, we could have even used the originally posted code along with Multiple Assignment like so:
a, b, c = "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
hash = {area: a, city: b, local: c}
#=> {:area=>"555", :city=>"333", :local=>"7777"}
OR
hash = {}
hash[:area], hash[:city], hash[:local] = "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
hash
#=> {:area=>"555", :city=>"333", :local=>"7777"}
OR along with zip and optionally to_h:
[:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
#=> [[:area, "555"], [:city, "333"], [:local, "7777"]]
([:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten).to_h
#=> {:area=>"555", :city=>"333", :local=>"7777"}

Squeeze double char in Ruby

What is the best way to squeeze multicharacter in string ?
Example:
hahahahahaha => ha
lalalala => la
awdawdawdawd => awd
str.squeeze("ha") # doesn't work
str.tr("haha", "ha") # doesn't work
def squeeze(s)
s.gsub(/(.+?)\1+/, '\1')
end
puts squeeze('hahahaha') # => 'ha'
puts squeeze('awdawdawd') # => 'awd'
puts squeeze('hahahaha something else') # => 'ha something else'
You can use regex based search and replace:
str.gsub(/(ha)+/, 'ha')

How do I use a ruby regex to get a substring?

I want to get the numbers out of strings such as:
person_3
person_34
person_356
city_4
city_15
etc...
It seems to me that the following should work:
string[/[0-9]*/]
but this always spits out an empty string.
[0-9]* successfully matches "0 or more" digits at the beginning of the string, so it returns "". [0-9]+ will match "1 or more" digits, and works as you expect:
irb(main):001:0> x = "test 92"
=> "test 92"
irb(main):003:0> x[/\d*/]
=> ""
irb(main):005:0> x.index(/\d*/)
=> 0
irb(main):004:0> x[/\d+/]
=> "92"

Ruby regular expressions

I understand how to check for a pattern in string with regexp in ruby. What I am confused about is how to save the pattern found in string as a separate string.
I thought I could say something like:
if string =~ /regexp/
pattern = string.grep(/regexp/)
and then I could be on with my life. However, this isn't working as expected and is returning the entire original string. Any advice?
You're looking for string.match() in ruby.
irb(main):003:0> a
=> "hi"
irb(main):004:0> a=~/(hi)/
=> 0
irb(main):005:0> a.match(/hi/)
=> #<MatchData:0x5b6e8>
irb(main):006:0> a.match(/hi/)[0]
=> "hi"
irb(main):007:0> a.match(/h(i)/)[1]
=> "i"
irb(main):008:0>
But also for working with what you just matched in the if condition you can use $& $1..$9 and $~ as such:
irb(main):009:0> if a =~ /h(i)/
irb(main):010:1> puts("%s %s %s %s"%[$&,$1,$~[0],$~[1]])
irb(main):011:1> end
hi i hi i
=> nil
irb(main):012:0>
You can also use the special variables $& and $1-$n, like so:
if "regex" =~ /reg(ex)/
puts $&
puts $1
end
Outputs:
regex
ex
$~ also contains the MatchData object. See also: http://www.regular-expressions.info/ruby.html.
I prefer some shortcuts like:
email = "Khaled Al Habache <khellls#gmail.com>"
email[/<(.*?)>/, 1] # => "khellls#gmail.com"

Resources