I found this code in a RailsCast:
def tag_names @tag_names || tags.map(&:name).join(' ') end
What does the (&:name)
in map(&:name)
mean?
I found this code in a RailsCast:
def tag_names @tag_names || tags.map(&:name).join(' ') end
What does the (&:name)
in map(&:name)
mean?
It's shorthand for tags.map(&:name.to_proc).join(' ')
If foo
is an object with a to_proc
method, then you can pass it to a method as &foo
, which will call foo.to_proc
and use that as the method's block.
The Symbol#to_proc
method was originally added by ActiveSupport but has been integrated into Ruby 1.8.7. This is its implementation:
class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end
Another cool shorthand, unknown to many, is
array.each(&method(:foo))
which is a shorthand for
array.each { |element| foo(element) }
By calling method(:foo)
we took a Method
object from self
that represents its foo
method, and used the &
to signify that it has a to_proc
method that converts it into a Proc
.
This is very useful when you want to do things point-free style. An example is to check if there is any string in an array that is equal to the string "foo"
. There is the conventional way:
["bar", "baz", "foo"].any? { |str| str == "foo" }
And there is the point-free way:
["bar", "baz", "foo"].any?(&"foo".method(:==))
The preferred way should be the most readable one.
It's equivalent to
def tag_names @tag_names || tags.map { |tag| tag.name }.join(' ') end
While let us also note that ampersand #to_proc
magic can work with any class, not just Symbol. Many Rubyists choose to define #to_proc
on Array class:
class Array def to_proc proc { |receiver| receiver.send *self } end end # And then... [ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ] #=> ["Hello world!", "Goodbye world!"]
Ampersand &
works by sending to_proc
message on its operand, which, in the above code, is of Array class. And since I defined #to_proc
method on Array, the line becomes:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
It's shorthand for tags.map { |tag| tag.name }.join(' ')
tags.map(&:name)
is The same as
tags.map{|tag| tag.name}
&:name
just uses the symbol as the method name to be called.
Josh Lee's answer is almost correct except that the equivalent Ruby code should have been as follows.
class Symbol def to_proc Proc.new do |receiver| receiver.send self end end end
not
class Symbol def to_proc Proc.new do |obj, *args| obj.send self, *args end end end
With this code, when print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
is executed, Ruby splits the first input [1,'a']
into 1 and 'a' to give obj
1 and args*
'a' to cause an error as Fixnum object 1 does not have the method self (which is :first).
When [[1,'a'],[2,'b'],[3,'c']].map(&:first)
is executed;
:first
is a Symbol object, so when &:first
is given to a map method as a parameter, Symbol#to_proc is invoked.
map sends call message to :first.to_proc with parameter [1,'a']
, e.g., :first.to_proc.call([1,'a'])
is executed.
to_proc procedure in Symbol class sends a send message to an array object ([1,'a']
) with parameter (:first), e.g., [1,'a'].send(:first)
is executed.
iterates over the rest of the elements in [[1,'a'],[2,'b'],[3,'c']]
object.
This is the same as executing [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
expression.
Two things are happening here, and it's important to understand both.
As described in other answers, the Symbol#to_proc
method is being called.
But the reason to_proc
is being called on the symbol is because it's being passed to map
as a block argument. Placing &
in front of an argument in a method call causes it to be passed this way. This is true for any Ruby method, not just map
with symbols.
def some_method(*args, &block) puts "args: #{args.inspect}" puts "block: #{block.inspect}" end some_method(:whatever) # args: [:whatever] # block: nil some_method(&:whatever) # args: [] # block: #&0x007fd23d010da8>
&reverse_upcase
&
&:name&:name.to_proc
&:name
&:name
&:name
&:to_sym.to_proc