Splatting Regex Matches

When extracting data from strings using regular expressions, Ruby’s splat operator, *, can come in handy.

To show how, let’s parse the string 'ios;version=7.0.4' into two variables: one containing the substring 'ios' and one containing '7.0.4'.

This is one way of doing it:

REGEX  = /^(.*);version=(.*)$/
string = 'ios;version=7.0.4'

matches = string.match(REGEX)
os      = matches[1]
version = matches[2]

os         #=> "ios"
version    #=> "7.0.4"

Another way of accomplishing the same thing is to use the * operator to splat out the returned matches.

REGEX  = /^(.*);version=(.*)$/
string = 'ios;version=7.0.4'

_, os, version = *string.match(REGEX)

os         #=> "ios"
version    #=> "7.0.4"

Let’s take a look at how this works behind the scenes.

In the example above, the string stored in the string variable matches the REGEX causing String#match to return a MatchData object.

matches = string.match(REGEX)

matches    #=> #<MatchData "ios;version=7.0.4" 1:"ios" 2:"7.0.4">

The MatchData object is then supplied as an argument to the splat operator. The splat operator calls the MatchData#to_a method to get an array representation of the matches.

matches.to_a    #=> ["ios;version=7.0.4", "ios", "7.0.4"]

After getting the array, the splat operator explodes it and assigns each of its elements to the corresponding variable on the left side of the assignment.

_, os, version = *['ios;version=7.0.4', 'ios', '7.0.4']

_          #=> "ios;version=7.0.4"
os         #=> "ios"
version    #=> "7.0.4"

Using _ as a variable name might seem strange but it’s completely valid to do so in Ruby. It’s also an idiomatic way of signaling to the reader that the variable’s value isn’t used.

We can use the fact that the splat operator calls to_a on its argument in our own classes like this:

class SplatMe
  def to_a
    [1, 2, 3]
  end
end

splat_me = SplatMe.new
puts *splat_me
#=> 1
#=> 2
#=> 3

But what happens when the string in the example above doesn’t match the REGEX?

REGEX  = /^(.*);version=(.*)$/
string = "does not match"

_, os, version = *string.match(REGEX)

os         #=> nil
version    #=> nil

If the string doesn’t match, match returns nil, the splat operator calls NilClass#to_a which always returns an empty array. Splatting an empty array over multiple variables assigns nil to all of them:

a, b, c = *[]

a    #=> nil
b    #=> nil
c    #=> nil