Ruby Regexp group matching, assign variables on 1 line

I'm currently trying to rexp a string into multiple variables. Example string:

ryan_string = "RyanOnRails: This is a test"

I've matched it with this regexp, with 3 groups:

ryan_group = ryan_string.scan(/(^.*)(:)(.*)/i)

Now to access each group I have to do something like this:

ryan_group[0][0] (first group) RyanOnRails
ryan_group[0][1] (second group) :
ryan_group[0][2] (third group) This is a test

This seems pretty ridiculous and it feels like I'm doing something wrong. I would be expect to be able to do something like this:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)

Is this possible? Or is there a better way than how I'm doing it?


Solution 1:

You don't want scan for this, as it makes little sense. You can use String#match which will return a MatchData object, you can then call #captures to return an Array of captures. Something like this:

#!/usr/bin/env ruby

string = "RyanOnRails: This is a test"
one, two, three = string.match(/(^.*)(:)(.*)/i).captures

p one   #=> "RyanOnRails"
p two   #=> ":"
p three #=> " This is a test"

Be aware that if no match is found, String#match will return nil, so something like this might work better:

if match = string.match(/(^.*)(:)(.*)/i)
  one, two, three = match.captures
end

Although scan does make little sense for this. It does still do the job, you just need to flatten the returned Array first. one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten

Solution 2:

You could use Match or =~ instead which would give you a single match and you could either access the match data the same way or just use the special match variables $1, $2, $3

Something like:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end

Solution 3:

You can name your captured matches

string = "RyanOnRails: This is a test"
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string
puts one, two, three

It doesn't work if you reverse the order of string and the regex.