44

I've seen these several times but I can't figure out how to use them. The pickaxe says that these are special shortcuts but I wasn't able to find the syntactical description.

I've seen them in such contexts:

[1,2,3].inject(:+)

to calculate sum for example.

7stud
  • 46,922
  • 14
  • 101
  • 127
Valentin V
  • 24,971
  • 33
  • 103
  • 152
  • 2
    Fyi your example is wrong, it should read `[1,2,3].inject(&:+)` – August Lilleaas Apr 23 '10 at 08:42
  • 3
    I double checked, the [1,2,3].inject(:+) definitely works. Why? – Valentin V Apr 23 '10 at 08:44
  • It must be a feature of the `inject` method, then. `inject(:+)` is not Symbol#to_proc, `:+` has no special meaning in the ruby language - it's just a symbol. – August Lilleaas Apr 23 '10 at 09:29
  • 4
    Yes, in ruby 1.8.7+ when no block is given inject uses its first argument as a method name (which is a lot faster than using Symbol#to_proc or a regular block, btw). – sepp2k Apr 23 '10 at 10:02
  • 1
    possible duplicate of http://stackoverflow.com/questions/1961030/ruby-ruby-on-rails-ampersand-colon-shortcut – James A. Rosen Apr 23 '10 at 12:55
  • See also [Understanding `[ClassOne, ClassTwo].each(&:my_method)`](http://StackOverflow.Com/q/99318/), [What does `map(&:name)` mean in Ruby?](http://StackOverflow.Com/q/1217088/), [What exactly is is this in ruby: `&:capitalize`](http://StackOverflow.Com/q/1792683/), [Ruby/Ruby on Rails ampersand colon shortcut](http://StackOverflow.Com/q/1961030/), [Ruby : `&:symbol` syntax](http://StackOverflow.Com/q/2096975/), [What is this `&:last` Ruby Construct Called?](http://StackOverflow.Com/q/2211751/), [What do you call the `&:` operator in Ruby?](http://StackOverflow.Com/q/2259775/), ... – Jörg W Mittag Dec 22 '10 at 22:32
  • ... [What does `map(&:name)` do in this Ruby code?](http://StackOverflow.Com/q/2388337/), [`&:views_count` in `Post.published.collect(&:views_count)`](http://StackOverflow.Com/q/3888044/) and [Ruby Proc Syntax](http://StackOverflow.Com/q/4512587/). – Jörg W Mittag Dec 22 '10 at 22:32
  • Thank you for this question, this is one of the hardest things to google for I've ever come across, on a par with coalesce in c# (?:). – Tim Abell Jun 24 '13 at 00:41
  • @7stud If you don't mind me asking; what it is that you're trying to do with the title? =/ – Ajedi32 Aug 28 '14 at 16:55
  • @Ajedi32, I was trying to change 'Et' looking thing I see to '&', but apparently SO can't parse an ampersand correctly. – 7stud Aug 29 '14 at 04:46
  • @7stud Weird, the ampersand looks fine for me. – Ajedi32 Aug 29 '14 at 11:54
  • @Ajedi32, what encoding is your browser set to? I tried UTF-8 and Latin1, but I still see an 'Et' like thing. I tried three different browsers, and I see the same thing in all of them. – 7stud Aug 29 '14 at 16:26
  • @7stud UTF-8 This is what it looks like for me: http://i.imgur.com/4OFmURI.png Maybe it's the fonts you're using? I have no idea. – Ajedi32 Aug 29 '14 at 16:39
  • @Ajedi32, This is what I see: https://www.flickr.com/gp/54877190@N08/i0p16v I tried changing the fonts in my browser, but that didn't do anything. Wouldn't the page's CSS control the fonts? – 7stud Aug 29 '14 at 17:05
  • @7stud I was mostly referring to what fonts are installed on your system. I'm certainly no expert in that area though. Maybe you could find out what UTF-8 character your browser thinks that is, then go from there. Or maybe ask about it on [su] or perhaps even [meta] (depending on whether or not StackOverflow is actually sending your browser the correct character)? – Ajedi32 Aug 29 '14 at 17:56
  • @Ajedi, The html sent to me by SO uses an html entity for the ampersand: `&`. I think you are right about it being related to my system's font for an ampersand. Apparently, an ampersand comes from the latin 'et', and the font I see harks back to those origins. – 7stud Aug 29 '14 at 18:25

2 Answers2

78

Let's start with an easier example. Say we have an array of strings we want to have in caps:

['foo', 'bar', 'blah'].map { |e| e.upcase }
# => ['FOO', 'BAR', 'BLAH']

Also, you can create so called Proc objects (closures):

block = proc { |e| e.upcase }
block.call("foo") # => "FOO"

You can pass such a proc to a method with the & syntax:

block = proc { |e| e.upcase }
['foo', 'bar', 'blah'].map(&block)
# => ['FOO', 'BAR', 'BLAH']

What this does, is call to_proc on block and then calls that for every block:

some_object = Object.new
def some_object.to_proc
  proc { |e| e.upcase }
end

['foo', 'bar', 'blah'].map(&some_object)
# => ['FOO', 'BAR', 'BLAH']

Now, Rails first added the to_proc method to Symbol, which later has been added to the ruby core library:

:whatever.to_proc # => proc { |e| e.whatever }

Therefore you can do this:

['foo', 'bar', 'blah'].map(&:upcase)
# => ['FOO', 'BAR', 'BLAH']

Also, Symbol#to_proc is even smarter, as it actually does the following:

:whatever.to_proc # => proc { |obj, *args| obj.send(:whatever, *args) }

This means that

[1, 2, 3].inject(&:+)

equals

[1, 2, 3].inject { |a, b| a + b }
user1816847
  • 1,877
  • 3
  • 17
  • 29
Konstantin Haase
  • 25,687
  • 2
  • 57
  • 59
3

inject accepts a symbol as a parameter, this symbol must be the name of a method or operator, which is this case is :+

so [1,2,3].inject(:+) is passing each value to the method specified by the symbol, hence summing all elements in the array.

source: https://ruby-doc.org/core-2.5.1/Enumerable.html

mundo03
  • 1
  • 2