When iterating over arrays, there's one piece of shorthand that I find myself using again and again. It's the &: trick, aka the "ampersand colon" or "pretzel colon". In case you're not familiar with it, here's how it works:
words = ["would", "you", "like", "to", "play", "a", "game?"]
# this...
words.map &:length
# ..is equivalent to this:
words.map { |w| w.length }
Until recently, I had assumed that the &: syntax was an operator. But it's not. It's a clever hack that started out in ActiveSupport and became an official feature in Ruby 1.8.7.
The & operator
In addition to being used for AND logic operations, the "&" character has another use in Ruby. When added to the beginning of a method argument, it calls to_proc on its operand and passes it in as a block. That's a mouthful. It's much simpler to see the example:
def my_method(&block)
block.call
end
class Greeter
def self.to_proc
Proc.new { "hello" }
end
end
my_method(&Greeter) # returns "hello"
Symbol#to_proc
You can add a to_proc
method to any object, including Symbol. That's exactly what ruby does to allow for the &:
shortcut. It looks something like this:
class Symbol
def to_proc
Proc.new do |item|
item.send self
end
end
end
Clear as mud? The important part is item.send(self)
. Self, in this case refers to the symbol.
Putting it all together
Enumberable methods like each
and map
accept a block. For each item they call the block and pass it a reference to the item. The block in this case is generated by calling to_proc on the symbol.
# &:name evaluates to a Proc, which does item.send(:name)
items.map(&:name)
The interesting thing about this is that map
doesn't know any of this is going on! The bulk of the work is being done by the :name
symbol. It's definitely clever...almost too clever for my taste. But it's been a part of Ruby's standard library for years at this point, and it's so handy I doubt I'll stop using it for now. :)