Using splats to build up and tear apart arrays in Ruby

One of the things that I love about Ruby is the depth of its features. You may use an operator, but do a little digging and you'll find that you've only been scratching the surface of what it's capable of. The humble splat operator (* and **) is a great example.

You've probably used splats for "catch-all" arguments. And that's all that most people use them for.

def go(x, *args)
  puts args.inspect
end

go("a", "b", "c")

If you are using the newfangled keyword argument syntax, use a double splat like so:

def go(**params)
  puts params.inspect
end

go(x: 100, y: 200)

This is useful, but you can use splats for a lot more. Let's dive in!

Using an array to pass multiple arguments

Not only can you use splats when defining methods, but you can also use them when calling methods. They let you pass an array into a function expecting multiple arguments. The first item in the array becomes the first argument, the second item becomes the second argument and so on.

def go(x, y)
end

point = [12, 10]
go(*point)

And don't forget that you can use a double splat for new style keyword arguments:

def go(x:, y:)
end

point = { x: 100, y: 200 }
go(**point)

The splat doesn't have to go at the end

While it's common to put your splatted arguments at the end of the argument list, there is no law requiring it.

You can put the splat anywhere in the argument list.

def go(x, *args, y)
  puts x # => 1
  puts y # => 5
  puts args.inspect # => [2,3,4]
end

go(1, 2, 3, 4, 5)

Array Destructuring

All of these tricks with arguments are just a special case of array destructuring.

In case you aren't familiar with the term "array destructuring," it simply means to break an array down into individual items. It looks like this:

a, b = [1, 2]
puts a
# 1
puts b
# 2

This works well but it can be a pain to have to specify a variable to hold every single item in the array. The splat operator gets around this - acting essentially like a wildcard. Lets take a look at a few examples.

Popping the first item from an array

Occasionally it's useful to be able to pop the first item off of an array without altering the original array. That's what this example does.

first, *remainder = [1, 2, 3, 4, 5]
first
# => 1
remainder
# => [2, 3, 4, 5]

If you just wanted the first item but not the rest of the array, you can use the syntax:

first, * = [1, 2, 3, 4, 5]
first
# => 1

Popping the last item

To pull the item off of the end of the array instead of the beginning, just stick the splat at the beginning like so:

*prefix, last = [1, 2, 3, 4, 5]
last
# => 5
prefix
# => [1, 2, 3, 4]

Again, if we don't want a specific variable, we don't have to assign it:

Get the first and last n items of an array

If you put the splat operator in the middle, you can pull an arbitrary number of items off of each end of the array.

first, *, last =  [1, 2, 3, 4, 5]
first
# => 1
last
# => 5

Limitations

When using the splat operator in array destructuring, you still have to specify the position of array items with respect to the beginning and end of the array. So it's not the best tool for extracting items from the middle of a long array.

Also, I can't seem to find any cool tricks using the double-splat (**) operator to mess with hashes. Lame!

Constructing arrays

The splat operator is useful not only for destructuring arrays but also for constructing them.

In the following example, we use splat to join two arrays.

[*[1,2], *[3,4]]
=> [1, 2, 3, 4]

This is the equivalent of [[1, 2], [3,4]].flatten.

If that were the end of the story, it wouldn't be very useful. But splat has another peculiar ability. It can intelligently coerce objects into arrays.

# Usually `*thing` wraps `thing` in an array
x = *"hi mom"
# => ["hi mom"]

# ...unless it's nil
x = *nil
# => []

# Arrays are passed through unchanged
x = *[1,2,3]
# => [1, 2, 3]

# But hashes are converted to arrays
x = *{a: 1}
# => [[:a, 1]]

This gives us a tool for building arrays without having to do a ton of manual type coercion.

For example, imagine you're collecting an array of strings for some configuration system. Normally you'd want to:

  • Check if the array exists, and initialize it if not
  • Respond intelligently if someone tries to add an array of strings, not just a single string

The splat operator gives us this for free:

# Your configuration hash may or may not have
# an existing :ignore array. 
config = { }

# The callback function might return an array
# or it might return a single item. 

def add_ignores
  "scoundrels" # could also be an array like ["scoundrels", "cads", "ne'er-do-wells"]
end

# This is where the magic happens. No matter what you 
# start with you get an array of things to ignore. 
config[:ignore] = [*config[:ignore], *add_ignores()]

That's it

I hope it's obvious that you probably shouldn't go rewrite all of your existing array-manipulation code to use splats. They're not always the right tool, and they can make code hard to read when used to frequently.

However, sometimes an array splat trick is exactly what you need. I hope, when that time comes, you'll try one out. :)