5

If we have a method on a collection that takes a block with another method executed on each element, we can write it shorter using ampersand. For example: if we have an array of integers and we want to remove the odd numbers we can do this:

[1,2,3,4,5,6,7,8].reject {|x| x.odd?}

Using ampersand, we can write this:

[1,2,3,4,5,6,7,8].reject(&:odd?)

Let's say we have an array of strings, and we want to remove the elements containing 'a'. We can write the solution like this:

['abc','cba','cbc','cdc','dca','cad','dc','cc].reject {|x| x.include? 'a'}

How do we write this using the ampersand syntax (if it's possible)?

sawa
  • 165,429
  • 45
  • 277
  • 381
Dmitri
  • 199
  • 8
  • 2
    This is not exactly possible although I have seen some pretty neat hacks to this concept.[Like this SO Answer](http://stackoverflow.com/a/23711606/1978251) What you are talking about is called `Symbol#to_proc` and googling pass arguments to `Symbol to_proc` will offer some insight but the out of the box answer is you don't. – engineersmnky Oct 12 '15 at 17:03
  • 1
    [Great discussion](https://bugs.ruby-lang.org/issues/9076) about another similar chaining concept from ruby-lang.org. While I disagree with the premise of further polluting the ampersand (`&`) in ruby the conversation has some merit and makes for a though provoking read. My main concern with any of these concepts is the further you take a syntactical sugar the less readable the code becomes and one of my favorite parts about ruby is it's emphasis on human readable code. – engineersmnky Oct 12 '15 at 17:16
  • Your "solution" is invalid. – sawa Oct 12 '15 at 17:55

1 Answers1

8

You can't, it is not possible.

& followed by a symbol is is a shortcut for a method that does not take any arguments.

As you said,

.reject(&:odd?)

Is a shortcut for:

 .reject {|x| x.odd?}

Basically, &:SYMBOL is always a shortcut for passing a block {|x| x.SYMBOL}

The reason this works is because calling the #to_proc method on a symbol returns a lambda expression that's a one-argument lambda, which calls the method with the same name as the symbol on it's argument.

And & converts from lambda to a block argument -- but beyond that, will call to_proc on it's operand before converting it to a block argument. So it calls to_proc on the symbol, and then gets a lambda from the symbol. And then passes this as a block argument to your method.

There's no way to use that shortcut when you need a block whose body goes beyond a one-argument block which just calls the method named after a symbol on it's argument.

Just write it out as you did. Nothing at all wrong with .reject {|x| x.include? 'a'}, just write that.

jrochkind
  • 22,799
  • 12
  • 59
  • 74