341

Possible Duplicate:
What does map(&:name) mean in Ruby?

In Ruby, I know that if I do:

some_objects.each(&:foo)

It's the same as

some_objects.each { |obj| obj.foo }

That is, &:foo creates the block { |obj| obj.foo }, turns it into a Proc, and passes it to each. Why does this work? Is it just a Ruby special case, or is there reason why this works as it does?

Community
  • 1
  • 1
Allan Grant
  • 3,839
  • 3
  • 19
  • 17
  • This is also referred to as [pretzel](http://blog.honeybadger.io/how-ruby-ampersand-colon-works/) [colon](http://technology.customink.com/blog/2015/06/08/ruby-pretzels/) syntax. – anothermh Jul 24 '17 at 21:33

2 Answers2

506

Your question is wrong, so to speak. What's happening here isn't "ampersand and colon", it's "ampersand and object". The colon in this case is for the symbol. So, there's & and there's :foo.

The & calls to_proc on the object, and passes it as a block to the method. In Ruby, to_proc is implemented on Symbol, so that these two calls are equivalent:

something {|i| i.foo }
something(&:foo)

So, to sum up: & calls to_proc on the object and passes it as a block to the method, and Ruby implements to_proc on Symbol.

August Lilleaas
  • 54,010
  • 13
  • 102
  • 111
  • 90
    More precisely: the ampersand unpacks the `Proc` object so that it gets passed as if it was a literal block. Only if the object is not *already* a `Proc` object, does it call `to_proc`. – Jörg W Mittag Dec 25 '09 at 14:36
  • Symbol#to_proc is only native in Ruby > 1.9 – Steve Graham Dec 25 '09 at 16:05
  • 7
    @Steve: No, it's in 1.8.7 as well. p RUBY_VERSION # => "1.8.7" p ["a", "b", "c"].map(&:upcase) # => ["A", "B", "C"] – August Lilleaas Dec 25 '09 at 16:24
  • you're right. it's not in http://ruby-doc.org/core/classes/Symbol.html for some weird reason though?! :S – Steve Graham Dec 25 '09 at 17:58
  • 1
    http://ruby-doc.org/core/ is for 1.8.7, http://ruby-doc.org/core-1.8.7 / is the 1.8.7 equivalent. Here's the entry: http://ruby-doc.org/core-1.8.7/classes/Symbol.html#M000086 – August Lilleaas Dec 26 '09 at 18:06
  • 1
    Thanks, that makes sense. Good to know that it's in Ruby 1.8.7 and 1.9. – Allan Grant Dec 26 '09 at 21:12
  • thanks august. do you know what version of ruby the default docs cover? i.e. /core not /core-1.8.7, /core-1.9. i always thought it was the latest release of 1.8? the way it is is not very helpful! – Steve Graham Dec 27 '09 at 22:48
  • It is explained on http://www.ruby-doc.org/. /core is 1.8.6, then there's /core-1.8.7/ and /ruby-1-9/ (yay for no convention). – August Lilleaas Dec 28 '09 at 00:34
  • @August: Did you mean "ruby-doc.org/core is for 1.8.6"? in your first comment? – Andrew Grimm Dec 02 '10 at 22:31
  • 2
    Thanks. This is the first time I've *actually* understood what &: was doing, despite using the convention for years. – David Hempy Jul 31 '17 at 17:42
  • Let's say foo has an attribute `name`, is it possible to apply the ampersand on the `foo.name`? e.g. in pseudo-code example: `something(&:foo.name)`. – null Jan 13 '18 at 20:10
  • So, how does the element, i, get passed to the block? And how does Ruby decide, it's the element of the enumerable, and not the enumerable itself? Is passing the element particular to enumerable objects? Is it applicable for non-enumerables also? – Abdullah Numan Jul 01 '23 at 01:33
  • there's no magic involved. It's completely up to the implementation of `something`, and it calls the provided block as if it was a function. – August Lilleaas Jul 17 '23 at 03:01
93

There's nothing special about the combination of the ampersand and the symbol. Here's an example that (ab)uses the regex:

class Regexp
  def to_proc
    ->(str) { self =~ str ; $1 }
  end
end
%w(station nation information).map &/(.*)ion/

=> ["stat", "nat", "informat"]

Or integers.

class Integer
  def to_proc
    ->(arr) { arr[self] }
  end
end

arr = [[*3..7],[*14..27],[*?a..?z]]
arr.map &4
=> [7, 18, "e"]

Who needs arr.map(&:fifth) when you have arr.map &4?

Michiel de Mare
  • 41,982
  • 29
  • 103
  • 134