The splat operator (*
) is the most flexible Ruby tool I’ve come across. Not sure how many inputs your method will receive? Want a flexible shortcut to create, join, or break up arrays? What about a handy way to pass arguments to methods? Splat can help.
Array manipulation
Fundamentally, the splat operator manipulates arrays. It builds, flattens, and separates arrays into component parts with minimal fuss.
Construction and conversion
Splat converts strings, numbers, floats, and hashes into arrays, either directly or through variables.
a = *"The barn"
#=> ["The barn"]
a = *1,2,3
or
a = *(1..3)
#=> [1, 2, 3]
f = *1.1, 2.2, 3.3
a = *f
#=> [1.1, 2.2, 3.3]
s = "How much hay is enough?"
a = *s
#=> ["How much hay is enough?"]
a = *{:Rabbits => 2, :Pigs => 3, :Hay => 0}
#=> [[:Rabbits, 2], [:Pigs, 3], [:Hay, 0]]
Interestingly, splat can also convert nil
into an empty array. Something we can’t do when calling Array.new(nil)
, for example.
a = *nil
#=> []
As you may have guessed, splat treats existing arrays as pass-through arguments.
a = *["pitch fork", "watering bucket"]
#=> ["pitch fork", "watering bucket"]
Flattening
Outside of creating new arrays, splats quickly flatten arrays.
a = *["ducks", "geese"], *["pigs", "cows"]
or
a = [*["ducks", "geese"], *["pigs", "cows"]]
#=> ["ducks", "geese", "pigs", "cows"]
poultry = *"ducks", "geese"
porcine_bovine = *"pigs", "cows"
a = *poultry, *porcine_bovine
#=> ["ducks", "geese", "pigs", "cows"]
Destructuring
One of my favorite splat uses is breaking down arrays into component parts, otherwise known as destructuring.
We know array elements can be assigned with commas:
small, medium, large = ["bantam chicken", "cayuga duck", "weeder goose"]
#=> small = "bantam chicken"
#=> medium = "cayuga duck"
#=> large = "weeder goose"
Splat can assign these values implicitly, in many positions:
small, *large = ["bantam chicken", "cayuga duck", "weeder goose", "pig"]
#=> small = "bantam chicken"
#=> large = ["cayuga duck", "weeder goose", "pig"]
*various, large = ["bantam chicken", "cayuga duck", "weeder goose", "pig"]
#=> various = ["bantam chicken","cayuga duck", "weeder goose"]
#=> large = "pig"
small, *mediums, large = ["bantam chicken", "cayuga duck", "weeder goose", "pig"]
#=> small = "bantam chicken"
#=> mediums = ["cayuga duck", "weeder goose"]
#=> large = "pig"
Flexible method definitions
Knowing that splats manipulate arrays, we see their most common use in method definitions, allowing methods to take an unspecified number of arguments through a single term.
def farm_chores(who, action, *animals)
animals.each.with_index(2) { |animal, index| puts "#{who} will #{action} the #{index} #{animal}"}
end
farm_chores("Farmer Brown", "feed", "ducks", "geese", "chickens", "pigs", "lambs")
#=> Farmer Brown will feed the 2 ducks
#=> Farmer Brown will feed the 3 geese
#=> Farmer Brown will feed the 4 chickens
#=> Farmer Brown will feed the 5 pigs
#=> Farmer Brown will feed the 6 lambs
The method above takes in a who
argument, an action
argument, and then as many animal
arguments as we can muster. In the background, *animals
is bundling all the arguments that aren’t who
and action
into an array, which is handy for iterating through an ever-growing list of chores.
Position agnostic
The splat argument can be placed in any position within the method definition. While the method above has an ending splat argument, the splat can also be the first or second argument.
The return values below are exactly the same as the the initial #farm_chores
method above. We simply adjust the method definition and call to use the new argument order.
def farm_chores(*animals, who, action)
animals.each.with_index(2) { |animal, index| puts "#{who} will #{action} the #{index} #{animal}"}
end
farm_chores("ducks", "geese", "chickens", "pigs", "lambs", "Farmer Brown", "feed")
def farm_chores(who, *animals, action)
animals.each.with_index(2) { |animal, index| puts "#{who} will #{action} the #{index} #{animal}"}
end
farm_chores("Farmer Brown", "ducks", "geese", "chickens", "pigs", "lambs", "feed")
Nested input
Splat input automatically accounts for nested arguments, as well. Passing an explicit array into the same #farm_chores
method doesn’t break anything; it simply returns the nested argument as an array.
farm_chores("Farmer Brown", "ducks", "geese", "chickens", ["chicks", "baby bunnies"], "feed")
#=> Farmer Brown will feed the 2 ducks
#=> Farmer Brown will feed the 3 geese
#=> Farmer Brown will feed the 4 chickens
#=> Farmer Brown will feed the 5 ["chicks", "baby bunnies"]
Excluded input
Beyond bundling important arguments, splat can separate out messy arguments that would otherwise throw a method call error.
If our method below might be called with many unknowns, and we only care about who
and action
, splat can help us gracefully focus on the key arguments without explicitly specifying the rest.
def farm_chores(who, action, *unused)
puts "#{who} will #{action} the animals every day."
end
farm_chores("Farmer Brown", "feed", "ducks", "geese", "chickens", "pigs", "lambs", "barns", "land", "water")
#=> Farmer Brown will feed the animals every day.
Flexible method calls
On the other side of defining methods, splat can also call a method using a single term that represents multiple arguments.
If we specify four arguments in a #two_chores
method definition, splat allows us to call the method with only three arguments, automatically feeding the animal
arguments.
def two_chores(who, action, animal_one, animal_two)
puts "#{who} will #{action} the #{animal_one} and #{animal_two}"
end
*animals = "pigs", "lambs"
two_chores("Farmer Brown", "feed", *animals)
#=> Farmer Brown will feed the pigs and lambs
Even if we explicitly define animals
as an array, splat will split the arguments for us, resulting in the same return values.
animals = ["pigs", "lambs"]
two_chores("Farmer Brown", "feed", *animals)
#=> Farmer Brown will feed the pigs and lambs
Better code
The splat operator offers Rubyists a shortcut for manipulating arrays and methods. Ultimately, splat allows us to build more resilient, flexible functionality with less code, a worthy goal for any programmer.
Sources
Thanks to the people below for sharing their splat discoveries.
- https://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/
- https://alexcastano.com/everything-about-ruby-splats/
- https://medium.freecodecamp.org/rubys-splat-and-double-splat-operators-ceb753329a78
- https://www.honeybadger.io/blog/ruby-splat-array-manipulation-destructuring/