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?
Solution 1
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
Solution 2
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.
Solution 3
It's equivalent to
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Solution 4
tags.map(&:name)
is The same as
tags.map{|tag| tag.name}
&:name just uses the symbol as the method name to be called.
Solution 5
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!' ) }
Solution 6
It's shorthand for tags.map { |tag| tag.name }.join(' ')
Solution 7
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: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
The Symbol gets converted to a Proc because it's passed in as a block. We can show this by trying to pass a proc to .map without the ampersand:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
Even though it doesn't need to be converted, the method won't know how to use it because it expects a block argument. Passing it with & gives .map the block it expects.
Solution 8
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;
:firstis a Symbol object, so when&:firstis 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.
Solution 9
(&:name) is short for (&:name.to_proc) it is same as tags.map{ |t| t.name }.join(' ')
to_proc is actually implemented in C
Solution 10
map(&:name) takes an enumerable object (tags in your case) and runs the name method for each element/tag, outputting each returned value from the method.
It is a shorthand for
array.map { |element| element.name }
which returns the array of element(tag) names
Solution 11
First, &:name is a shortcut for &:name.to_proc, where :name.to_proc returns a Proc (something that is similar, but not identical to a lambda) that when called with an object as (first) argument, calls the name method on that object.
Second, while & in def foo(&block) ... end converts a block passed to foo to a Proc, it does the opposite when applied to a Proc.
Thus, &:name.to_proc is a block that takes an object as argument and calls the name method on it, i. e. { |o| o.name }.
Solution 12
Although we have great answers already, looking through a perspective of a beginner I'd like to add the additional information:
What does map(&:name) mean in Ruby?
This means, that you are passing another method as parameter to the map function. (In reality you're passing a symbol that gets converted into a proc. But this isn't that important in this particular case).
What is important is that you have a method named name that will be used by the map method as an argument instead of the traditional block style.
Solution 13
It basically execute the method call tag.name on each tags in the array.
It is a simplified ruby shorthand.
Solution 14
Here :name is the symbol which point to the method name of tag object.
When we pass &:name to map, it will treat name as a proc object.
For short, tags.map(&:name) acts as:
tags.map do |tag|
tag.name
end
Solution 15
it means
array.each(&:to_sym.to_proc)
Solution 16
It is same as below:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
