Over the next week or so I’ll be sharing Ruby idioms and flourishes that I quite like. Today I’d I’ll show a few tiny uses of splat! that make me tremble with delight.
Splat! – For Beginners
Splat! is the star (*) operator, typically used in Ruby for defining methods that take an unlimited number of arguments:
def sprintf(string, *args)
end
It can also be used to convert an array to the multiple-argument form when invoking a function:
some_ints = [1,2,3]
sprintf("%%i %%i %%i", *some_ints)
Splat! – For Wizards
Array to Hash Conversion
The best use of splat! for invoking a infinite-arity functions I’ve ever seen is the recipe for converting an array to a hash. Suppose you have an array of pairs:
array = [[key_1, value_1], [key_2, value_2], ... [key_n, value_n]]
You would like to produce from it the hash: {key1 => value1 ... } You could inject down the array, everybody loves inject, but there is a better way:
Hash[*array.flatten]
Amazing right? This relies on the the fact that the Hash class implements the [] (brackets) operator and behaves thusly:
Hash[key1, value1, ...] = { key1 => value1, ... }
Heads or tails?
Splat! can be used for more than just method definition and invocation. My personal favorite use is destructuring assignment. I read this in Active Record’s source code recently:
def sanitize_sql_array(ary)
statement, *values = ary
...
end
This is invoked when you do something like User.find(:all, :conditions => ['first_name = ? and last_name = ?', 'nick', 'kallen']). Splat! is used here is to get the head and tail of the conditions array. Of course, you could use always use shift, but the functional style used here is quite beautiful. Consider another example:
first, second, *rest = ary
One final trivium (#to_splat aka #to_ary)
You can actually customize the behavior of the splat operator. In Ruby 1.8, implement #to_ary and in 1.9 it’s #to_splat. For example
class Foo
def to_ary
[1,2,3]
end
end
a, *b = Foo.new
a # => 1
b # => [2,3]
This also works for method invocation:
some_method(*Foo.new) == some_method(1,2,3)
When I first learned this at RubyConf I thought this was mind-blowing. I have since never used it.
I recently posted similar question to the forum http://www.ruby-forum.com/topic/150123#new
Thanks for the series.
April 23, 2008 at 1:28 pm
My favourite:
regex = /([^ ]*) ([^ ]*)/
match, first_name, last_name = *”Tom Ward”.match(regex)
April 23, 2008 at 2:02 pm
Or better formatted (hopefully):
regex = /([^ ]*) ([^ ]*)/
match, first_name, last_name = *”Tom Ward”.match(regex)
April 23, 2008 at 2:03 pm