13

Is it possible to concat two regex variables in Ruby?

r1 = /my_str/
r2 = /my_str1/
r3 = r1+r2

Can anyone give any suggestions?

Bob Gilmore
  • 12,608
  • 13
  • 46
  • 53
Swapnil
  • 191
  • 2
  • 9
  • 2
    what is the goal of concatenation? what should be matched by such regex? – RomanPerekhrest Jan 18 '17 at 07:36
  • Is it JavaScript? The answer for the current question is either no, but there are ways depending on the language. For JS, see [*How can I concatenate regex literals in JavaScript?*](http://stackoverflow.com/questions/185510/how-can-i-concatenate-regex-literals-in-javascript). – Wiktor Stribiżew Jan 18 '17 at 07:36
  • @WiktorStribiżew The question's been edited to specify Ruby. Can you re-consider your closure to see whether the dupe still applies? – TylerH Jan 18 '17 at 20:59

5 Answers5

10

Regexp::union

r1 = /my_str/
r2 = /my_str1/
r3 = Regexp.union(r1, r2)
steenslag
  • 79,051
  • 16
  • 138
  • 171
  • 5
    I don't believe this is the solution OP wants. `Regexp#union` does a regexp "or", i.e. /my_str|my_str1/. I think OP wants /my_strmy_str1/ or /my_str.*my_str1/. – Pistos Mar 19 '19 at 04:35
10

Concatenate the sources and pass to Regexp.new:

2.4.1 :009 > r1 = /a./
 => /a./ 
2.4.1 :010 > r2 = /b{3}/
 => /b{3}/ 
2.4.1 :011 > r3 = Regexp.new(r1.source + r2.source)
 => /a.b{3}/ 
2.4.1 :022 > "axbbb" =~ r3
 => 0 
2.4.1 :023 > "axbb" =~ r3
 => nil 
Pistos
  • 23,070
  • 14
  • 64
  • 77
2

Contra other answers, it's not strictly necessary to call source; in many cases Regexp#to_s works just as well. From the docs:

Returns a string containing the regular expression and its options (using the ?(opts:source) notation. This string can be fed back in to ::new to a regular expression with the same semantics as the original.

So often it's enough to just use string interpolation:

r1 = /my_str1/
# => /my_str1/ 
r2 = /my_str2/
# => /my_str2/ 
r3 = Regexp.new("#{r1}|#{r2}")
# => /(?-mix:my_str1)|(?-mix:my_str2)/ 

The result is less readable than the /my_str1|my_str2/, but will match identically -- the tradeoff is more readable source for a less readable regexp.

That said, if you want to apply different options to the combined expression (or parts of it) than to the original expressions, you will need to call source.

r1 = /[a-z]\n[0-9]/
r2 = /[0-9]\n[a-z]/

r3 = Regexp.new("(?mi-x:(#{r1.source}|#{r2.source}))")
# => /(?mi-x:([a-z]\n[0-9]|[0-9]\n[a-z]))/
r3.match("A\n1")
# => #<MatchData "A\n1" 1:"A\n1">        <-- works

r4 = Regexp.new("(?mi-x:(#{r1}|#{r2}))")
# => /(?mi-x:((?-mix:[a-z]\n[0-9])|(?-mix:[0-9]\n[a-z])))/ 
r4.match("A\n1")
# => nil                                 <-- doesn't work b/c wrong options
David Moles
  • 48,006
  • 27
  • 136
  • 235
1

Following works in Ruby - but I think it's not beautiful:

2.5.3 :001 > r1 = /my_str1/
 => /my_str/
2.5.3 :002 > r2 = /my_str2/
 => /my_str1/
2.5.3 :003 > r3 = Regexp.new( "#{r1.source}|#{r2.source}" )
 => /my_str1|my_str2/

Of course you even could implement + on Regexp yourself, like this (but of course totally not recommended ^^):

class Regexp
  def +(regexp)
    self.class.new("#{source}|#{regexp.source}")
  end
end

Then you can do:

2.5.3 :004 >     class Regexp
2.5.3 :005?>       def +(regexp)
2.5.3 :006?>         self.class.new("#{source}|#{regexp.source}")
2.5.3 :007?>       end
2.5.3 :008?>     end
 => :+
2.5.3 :009 > r1 + r2
 => /my_str1|my_str2/

There are of course reasons, why this is not implemented. I just assumed a or as concatenation (that should at least work in any cases - even when as mostly it's recommended definite start and end are defined in Regexp). But most probable you have some very different use case. So when you want to concatenate in different way, you maybe have to ensure that in first regexp \Zand in second \A has to be removed...

bpieck
  • 241
  • 1
  • 6
-1

If you have two regular expression , you can concatenate them like this:

var r1 = /my_str/;

var r2 = /my_str1/;

var r3 = new RegExp( r1.source + r2.source );

Community
  • 1
  • 1
Dasrath
  • 366
  • 2
  • 11