Last Updated: February 25, 2016
·
2.333K
· jedschneider

First, *Rest: list processing in Ruby

Context

In functional languages lists are often manipulated by getting the first argument and then associating the remainder of the list to a separate local for future processing. In clojure the API looks like:

(rest [1 2 3]) ; => (2,3)
(first [1 2 3]) ; => 1

Splat Arguments for array-ifying an expression

Such idoms are often nice in OO languages when dealing with array like returns. For example, a regular expression in Ruby:

regex = /(\d{3})/
# the normal way
match_data = "555-867-5309".match(regex)
# normally we don't really care about the match data
# what we really want are the memoized matches
match_data[1] #=> "555"

# splat the return
match_data, area_code = *"555-867-5309".match(regex)
area_code #=> "555"

So in the previous code snippet we have used the splat operator to 'cast' the return of String#match into an array and then destructuring that array with parallel assignment.

First, Rest Assignment in Ruby

In a similar vein, we can also use splat args on the assignment side to mimic the functional idiom as demonstrated in clojure:

# first, rest
a, *b = [1,2,3]

a #=> 1
b # => [2,3]

# rest, last
*a, b = [1,2,3]
a #=> [1,2]
b #=> 3

Application

Returning to our Regular Expression example:

#works even better with more captures
regex = /(\d{3})-(\d{3})-(\d{4})/
match, area_code, *locals = *"555-867-5309".match(regex)
locals #=> ["867", "5309"]
area_code #=> "555"

How can we use this in other situations?

Take a query in Rails

users = Users.where(...)
first_user = users.first #=> this is a query
other_users = users[1..-1] #=> back to the database

# or we could just go to the db once
first_user, *others = Users.where(...) #=> one query

Happy Splatting!