I need to match a url path with that contain any of 2 words vendor_tracks or shop_types, but shop types should be followed by a ' / '
My current REGEX is
//(vendor_tracks|shop_types)/
, but this match if contain shop_types/22
I need my Regex to match something like :
/shop_types?q%5Btrack_department
but NOT the below url
/shop_types/27/list_pinned_vendors
My current accepts both, while I need it to accept only the first. I tried many different methods to exclude the "shop_types" followed by / but always get escaped backslash error. Any solution for this? or alternative REGEX
You can use lookarounds to create custom restrictions
>> str1 = '/shop_types?q%5Btrack_department'
=> "/shop_types?q%5Btrack_department"
>> str2 = '/shop_types/27/list_pinned_vendors'
=> "/shop_types/27/list_pinned_vendors"
>> str1.match?(/shop_types(?!\/)/)
=> true
>> str2.match?(/shop_types(?!\/)/)
=> false
Here (?!\/) is negative lookahead which says that / character cannot be immediate character after shop_types
Note that end of string will also satisfy this condition as that would imply that there is no / character after shop_types
I tried many different methods to exclude the "shop_types" followed by / but always get escaped backslash error
You can use %r to define a regexp with alternate delimiters
# here the condition has also been changed
# restriction is that / shouldn't occur anywhere after matching shop_types
>> str1.match?(%r{shop_types(?!.*/)})
=> true
>> str2.match?(%r{shop_types(?!.*/)})
=> false
Related
I have a string, which describe some word, I must change ending of it to "sd", if ending == "jk".
For an example, I have word: "lazerjk", I need to get from it "lazersd".
I tried to use method .gsub!, but it doesn't work correctly if we have more than one occurrence of substring "jk" in a word.
String#rindex returns the index of the last occurrence of the given substring
String#[]= can take two integers arguments, first is index where start to replace and second - length of replaced string
You can use them this way:
replaced = "foo"
replacing = "booo"
string = "foo bar foo baz"
string[string.rindex(replaced), replaced.size] = replacing
string
# => "foo bar booo baz"
"jughjkjkjk\njk".sub(/jk$\z/, 'sd')
=> "jughjkjkjk\nsd"
without $ is probably sufficient.
It sounds like you're looking to replace a specific suffix only. If so, I would probably suggest using sub along with an anchored regex (to check for the desired characters only at the end of the string):
string_1 = "lazerjk"
string_2 = "lazerjk\njk"
string_3 = "lazerjkr"
string_1.sub(/jk\z/, "sd")
#=> "lazersd"
string_2.sub(/jk\z/, "sd")
#=> "lazerjk\nsd"
string_3.sub(/jk\z/, "sd")
#=> "lazerjkr"
Or, you could do without a regex at all by using the reverse! method along with a simple conditional statement to sub! only when the suffix is present:
string = "lazerjk"
old_suffix = "jk"
new_suffix = "sd"
string.reverse!.sub!(old_suffix.reverse, new_suffix.reverse).reverse! if string.end_with? (old_suffix)
string
#=> "lazersd"
OR, you could even use a completely different approach. Here's an example using chomp to remove the unwanted suffix and then ljust to pad the desired suffix to the modified string.
string = "lazerjk"
string.chomp("jk").ljust(string.length, "sd")
#=> "lazersd"
Note that the new suffix only gets added if the length of the string was modified with the initial chomp. Otherwise, the string remains unchanged.
If the goal is to substitute the LAST OCCURRENCE (as opposed to suffix only), then this could be accomplished by using sub along with reverse:
string = "jklazerjkm"
old_substring = "jk"
new_substring = "sd"
string.reverse.sub(old_substring.reverse, new_substring.reverse).reverse
#=> "jklazersdm"
Replacing "jk" at the end of a string with something else is straightforward and can be addressed without concern for other instances of "jk" that may be in the string, so I assume that is not what is being asked. Rather, I assume the problem is to replace the last instance of "jk" in a string with "sd".
Here are two solutions that make use of String#sub with a regular expression.
Use a negative lookahead
The idea here is to match "jk" provided it is not followed later in the string by another instance of "jk".
"lajkz\nejkrjklm".sub(/jk(?!.*jk)/m, "sd")
#=> "lajkz\nejkrsdlm"
Capture the part of the string that precedes the last "jk"
The match, if there is one, consists of the front of the string followed by the last "jk", which is replaced by the captured string followed by "sd".
"lajkz\nejkrjklm".sub(/\A(.*)jk/m) { $1 + "sd" }
#=> "lajkz\nejkrsdlm"
The two regular expressions can be written in free-spacing mode to make them self-documenting. The first is the following.
/
jk # match literal
(?! # begin a negative lookahead
.* # match zero or more characters other than line terminators
jk # match literal
) # end negative lookahead
/mx # invoke multiline and free-spacing regex definition modes.
Multiline mode causes . to match any character, including a line terminator.
The second regular expression can be written as follows.
\A # match the beginning of the string
(.*) # match zero or more characters other than line terminators
# and save the match to capture group 1
jk # match literal
/mx # invoke multiline and free-spacing regex definition modes.
Note that in both expressions .* is greedy, meaning that it will match as many characters as possible, including "jk" so long as other requirements of the expression are met, here that the last instance of "jk" in the string is matched.
Here is a different solution:
str = "jughjkjkjk\njk"
pattern = "jk"
replace_with = "sd"
str = str.reverse.sub(pattern.reverse, replace_with.reverse).reverse
I have a name spaced class..
"CommonCar::RedTrunk"
I need to convert it to an underscored string "common_car_red_trunk", but when I use
"CommonCar::RedTrunk".underscore, I get "common_car/red_trunk" instead.
Is there another method to accomplish what I need?
Solutions:
"CommonCar::RedTrunk".gsub(':', '').underscore
or:
"CommonCar::RedTrunk".sub('::', '').underscore
or:
"CommonCar::RedTrunk".tr(':', '').underscore
Alternate:
Or turn any of these around and do the underscore() first, followed by whatever method you want to use to replace "/" with "_".
Explanation:
While all of these methods look basically the same, there are subtle differences that can be very impactful.
In short:
gsub() – uses a regex to do pattern matching, therefore, it's finding any occurrence of ":" and replacing it with "".
sub() – uses a regex to do pattern matching, similarly to gsub(), with the exception that it's only finding the first occurrence (the "g" in gsub() meaning "global"). This is why when using that method, it was necessary to use "::", otherwise a single ":" would have been left. Keep in mind with this method, it will only work with a single-nested namespace. Meaning "CommonCar::RedTrunk::BigWheels" would have been transformed to "CommonCarRedTrunk::BigWheels".
tr() – uses the string parameters as arrays of single character replacments. In this case, because we're only replacing a single character, it'll work identically to gsub(). However, if you wanted to replace "on" with "EX", for example, gsub("on", "EX") would produce "CommEXCar::RedTrunk" while tr("on", "EX") would produce "CEmmEXCar::RedTruXk".
Docs:
https://apidock.com/ruby/String/gsub
https://apidock.com/ruby/String/sub
https://apidock.com/ruby/String/tr
This is a pure-Ruby solution.
r = /(?<=[a-z])(?=[A-Z])|::/
"CommonCar::RedTrunk".gsub(r, '_').downcase
#=> "common_car_red_trunk"
See (the first form of) String#gsub and String#downcase.
The regular expression can be made self-documenting by writing it in free-spacing mode:
r = /
(?<=[a-z]) # assert that the previous character is lower-case
(?=[A-Z]) # assert that the following character is upper-case
| # or
:: # match '::'
/x # free-spacing regex definition mode
(?<=[a-z]) is a positive lookbehind; (?=[A-Z]) is a positive lookahead.
Note that /(?<=[a-z])(?=[A-Z])/ matches an empty ("zero-width") string. r matches, for example, the empty string between 'Common' and 'Car', because it is preceeded by a lower-case letter and followed by an upper-case letter.
I don't know Rails but I'm guessing you could write
"CommonCar::RedTrunk".delete(':').underscore
I have a string aString = "old_tag1,old_tag2,'new_tag1','new_tag2'"
I want to replace the apostrophees that come right before or right after a comma. For example in my case the apostrophees enclosing new_tag1 and new_tag2 should be removed.
This is what I have right now
aString = aString.gsub("'", "")
This is however problematic as it removes any apostrophe inside for example if I had 'my_tag's' instead of 'new_tag1'. How do I get rid of only the apostrophes that come before or after the commas ?
My desired output is
aString = "old_tag1,old_tag2,new_tag1,new_tag2"
My guess is to use regex as well, but in a slightly other way:
aString = "old_tag1,old_tag2,'new_tag1','new_tag2','new_tag3','new_tag4's'"
aString.gsub /(?<=^|,)'(.*?)'(?=,|$)/, '\1\2\3'
#=> "old_tag1,old_tag2,new_tag1,new_tag2,new_tag3,new_tag4's"
The idea is to find a substring with bounding apostrophes and paste it back without it.
regex = /
(?<=^|,) # watch for start of the line or comma before
' # find an apostrophe
(.*?) # get everything between apostrophes in a non-greedy way
' # find a closing apostrophe
(?=,|$) # watch after for the comma or the end of the string
/x
The replacement part just paste back the content of the first, second, and third groups (everything between parenthesis).
Thanks for #Cary for /x modificator for regexes, I didn't know about it! Extremely useful for explanation.
This answers the question, "I want to replace the apostrophes that come right before or right after a comma".
r = /
(?<=,) # match a comma in a positive lookbehind
\' # match an apostrophe
| # or
\' # match an apostrophe
(?=,) # match a comma in a positive lookahead
/x # free-spacing regex definition mode
aString = "old_tag1,x'old_tag2'x,x'old_tag3','new_tag1','new_tag2'"
aString.gsub(r, '')
#=> => "old_tag1,x'old_tag2'x,x'old_tag3,new_tag1,new_tag2'"
If the objective is instead to remove single quotes enclosing a substring when the left quote is at the the beginning of the string or is immediately preceded by a comma and the right quote is at the end of the string or is immediately followed by comma, several approaches are possible. One is to use a single, modified regex, as #Dimitry has done. Another is to split the string on commas, process each string in the resulting array and them join the modified substrings, separated by commas.
r = /
\A # match beginning of string
\' # match single quote
.* # match zero or more characters
\' # match single quote
\z # match end of string
/x # free-spacing regex definition mode
aString.split(',').map { |s| (s =~ r) ? s[1..-2] : s }.join(',')
#=> "old_tag1,x'old_tag2'x,x'old_tag3',new_tag1,new_tag2"
Note:
arr = aString.split(',')
#=> ["old_tag1", "x'old_tag2'x", "x'old_tag3'", "'new_tag1'", "'new_tag2'"]
"old_tag1" =~ r #=> nil
"x'old_tag2'x" =~ r #=> nil
"x'old_tag3'" =~ r #=> nil
"'new_tag1'" =~ r #=> 0
"'new_tag2'" =~ r #=> 0
Non regex replacement
Regular expressions can get really ugly. There is a simple way to do it with just string replacement: search for the pattern ,' and ', and replace with ,
aString.gsub(",'", ",").gsub("',", ",")
=> "old_tag1,old_tag2,new_tag1,new_tag2'"
This leaves the trailing ', but that is easy to remove with .chomp("'"). A leading ' can be removed with a simple regex .gsub(/^'/, "")
In a Rails app I am reading a file with key/values. An index number is embedded in the key name, and I'd like to remove it, along with one of the spacing underscores.
So in the sample data below, I'd like to convert:
PRIMER_LEFT_1_END_STABILITY into PRIMER_LEFT_END_STABILITY
PRIMER_RIGHT_1_END_STABILITY into PRIMER_RIGHT_END_STABILITY
PRIMER_PAIR_1_COMPL_ANY_TH into PRIMER_PAIR_COMPL_ANY_TH
Sample Data
PRIMER_LEFT_1_END_STABILITY=7.2000
PRIMER_RIGHT_1_END_STABILITY=7.9000
PRIMER_PAIR_1_COMPL_ANY_TH=0.00
EDIT
Thanks to #tihom for the first answer. It's partially working, but I did not specify that the embedded integer can be of any value. When over 1 digit in length the regex fails:
1.9.3-p327 :003 > "PRIMER_LEFT_221_END_STABILITY".sub(/_\d/,"")
=> "PRIMER_LEFT21_END_STABILITY"
1.9.3-p327 :004 > "PRIMER_LEFT_21_END_STABILITY".sub(/_\d/,"")
=> "PRIMER_LEFT1_END_STABILITY"
To remove the first occurrence use sub else to remove all occurrences use gsub
"PRIMER_LEFT_1_END_STABILITY".sub(/_(\d)+/,"") # => "PRIMER_LEFT_END_STABILITY"
"+" matches one or more of the preceding character. So in this case it matches one or more of any digit followed by a "_"
You can use String#tr and String#squeezeas below :
ar=['PRIMER_LEFT_1_END_STABILITY','PRIMER_RIGHT_1_END_STABILITY','PRIMER_PAIR_1_COMPL_ANY_TH']
p ar.map{|s| s.tr('0-9','').squeeze("_")}
# => ["PRIMER_LEFT_END_STABILITY", "PRIMER_RIGHT_-END_STABILITY", "PRIMER_PAIR_COMPL_ANY_TH"]
ar=["PRIMER_LEFT_221_END_STABILITY","PRIMER_LEFT_21_END_STABILITY"]
p ar.map{|s| s.tr('0-9','').squeeze("_")}
# => ["PRIMER_LEFT_END_STABILITY", "PRIMER_LEFT_END_STABILITY"]
Suppose I have string variables like following:
s1="10$"
s2="10$ I am a student"
s3="10$Good"
s4="10$ Nice weekend!"
As you see above, s2 and s4 have white space(s) after 10$ .
Generally, I would like to have a way to check if a string start with 10$ and have white-space(s) after 10$ . For example, The rule should find s2 and s4 in my above case. how to define such rule to check if a string start with '10$' and have white space(s) after?
What I mean is something like s2.RULE? should return true or false to tell if it is the matched string.
---------- update -------------------
please also tell the solution if 10# is used instead of 10$
You can do this using Regular Expressions (Ruby has Perl-style regular expressions, to be exact).
# For ease of demonstration, I've moved your strings into an array
strings = [
"10$",
"10$ I am a student",
"10$Good",
"10$ Nice weekend!"
]
p strings.find_all { |s| s =~ /\A10\$[ \t]+/ }
The regular expression breaks down like this:
The / at the beginning and the end tell Ruby that everything in between is part of the regular expression
\A matches the beginning of a string
The 10 is matched verbatim
\$ means to match a $ verbatim. We need to escape it since $ has a special meaning in regular expressions.
[ \t]+ means "match at least one blank and/or tab"
So this regular expressions says "Match every string that starts with 10$ followed by at least one blank or tab character". Using the =~ you can test strings in Ruby against this expression. =~ will return a non-nil value, which evaluates to true if used in a conditional like if.
Edit: Updated white space matching as per Asmageddon's suggestion.
this works:
"10$ " =~ /^10\$ +/
and returns either nil when false or 0 when true. Thanks to Ruby's rule, you can use it directly.
Use a regular expression like this one:
/10\$\s+/
EDIT
If you use =~ for matching, note that
The =~ operator returns the character position in the string of the
start of the match
So it might return 0 to denote a match. Only a return of nil means no match.
See for example http://www.regular-expressions.info/ruby.html on a regular expression tutorial for ruby.
If you want to proceed to cases with $ and # then try this regular expression:
/^10[\$#] +/