January 1, 2015 by Daniel P. Clark

When to use Ampersand and/or Colon with Proc, Map, and Inject

So with Ruby permitting Procs called on Objects I’ve found sometimes a Colon Method will work, and sometimes you need a Ampersand Colon Method.  For example, when I map &:upcase on a list of strings it works.

But if I try without the Ampersand I get:

So for mapping it seems we need Ampersand.  And yes I’ve tested this with many methods and they are the same.  But let’s look at inject.

Here you can see both with and without Ampersand the concat method works.  The reason for this is because of how inject works.  Inject literally put the command between the values.

So inject is always passing the next value in the array as a parameter and calls the method on the previous Array item.

When you see the Ampersand Proc &:method the way to think of it is the Object it’s being called on substitutes the Ampersand.  So [“a”].map(&:upcase) will go over each item in the Array and place it in place of the Ampersand:

So a good way to think about Ampersand is that’s where the Object will get placed.  Map drops the method on each item with &: and inject will place the method between A and B with either &: or :

But wait!  There is another use case of Proc being used with Ampersand.

Here we have a method that takes a block, so we can put Ampersand in front of the method without a Colon.  But as you can see here it does something rather weird with inject.  I tried to reproduce how inject was calling this by manually putting the &putsy Proc in, but I had no luck in getting the same result.  So that remains a mystery to me.  But we know with map the Proc gets handed to each element cleanly:

We can try this same behaviour with a Lambda.

Here we can see inject blows up all-together.  With this we know that we should make it a habit using inject with only a Colon.  Think of inject as a method chain.  You’re sticking the method in-between, so you won’t be using Procs in practice for inject… just methods for chaining.

So when calling map with a Proc parameter (and not a block) know to use &: for calling the method on the Object.  And just & for passing the Object as a block.

And that should give you a great idea of when to use Ampersand and Colon’s with Procs, Maps, and Inject.

Visual  Guide

 

UPDATE

(Inject, Procs, and Lambdas)

After some insightful input from comments both here and on Reddit I’ll clarify and update the use of Procs and Lambdas with Inject and Injects ability.

The problem with my previous Inject examples is that I had written my procs and lambdas to only handle one input variable.  Inject deals with pairs at a time.  Here’s an example:

As has clarified for me in the comments Lambda’s are strict in how many parameters you give them.  However many you define them to take they will.  Procs are a little looser in allowing additional parameters.

From the above example we see that Procs and Lambdas work perfectly well with inject when they are designed to handle 2 parameters.  This is quite useful.  So now for Inject I suggest Colon (:) for Methods and Ampersand (&) for procs/lambdas.  For map it will either be both &: for method calls or & for procs/lambdas.

You can also use procs designed to handle multiple values with nested Arrays:

For a last note on Inject I will quote Vincent Franco

Injects are easier to understand if you think of them as folds(a functional construct.) So think of inject working like this: ((((a,b),c),d),e) which is slightly different than the way you express them here.

I thank everyone for their feedback; both constructive or otherwise.  It has been helpful for me and in turn for others as well.  I hope this information was both insightful and useful for you! Please comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

#howto#inject#lambda#map#proc#ruby#syntax

7
Leave a Reply

avatar
3 Comment threads
4 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
4 Comment authors
MattVincent FrancoDaniel P. ClarkJeff Dickey Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Jeff Dickey
Guest

Nice summary. BTW, I suspect you’ll get more keyword hits for `lambda` than `lamda`. 🙂

Daniel P. Clark
Guest

Thanks!

Vincent Franco
Guest
Vincent Franco

There are some issues with the logic in this article. First: [‘a’,’b’,’c’].inject(&putsy) “But as you can see here it does something rather weird with inject.” Reason: Inject is an accumulator that assigns expression of your block to memo. The reason you are getting “a” and then nothing is because the result of (puts “a”) is nil. So you are assigning nil to next memo, from there you are nil for every iteration of the accumulator. Because you are using the api of inject incorrectly (A proc with 1 arg). Second: [‘a’,’b’,’c’].inject(&lamsy) Reason: Again accumulator… Slight difference this time. You are… Read more »

Daniel P. Clark
Guest

Thank you for your input! Your insights do clear quite a bit up for me. When I was trying to duplicate injects behavior I was experimenting with inline consecutive Procs/Lambdas but not getting the same results. As you have shown I was not performing the same way that inject was performing it. Also according to some of the Reddit Feedback I was a bit brash in saying “With this we know that we should make it a habit using inject with only a Colon” after the lambda blew up. I wasn’t concluding that because of the lambda, I meant it… Read more »

Matt
Guest
Matt

Under your Visual Guide, I think you have a typo in that you’re using brackets instead of parentheses. You have:

[my_proc[“a”], my_proc[“b”], my_proc[“c”]]

whereas I think you mean:

[my_proc(“a”), my_proc(“b”), my_proc(“c”)]

Daniel P. Clark
Guest

Ruby uses [] to be the same as .call() on Proc objects. So my_proc[“a”] is equivalent to my_proc.call(“a”).

Matt
Guest
Matt

Very interesting, that’s news to me! Thanks for pointing that out. 🙂