Other Ruby Map Shorthand Notation

trh178 picture trh178 · Jan 9, 2012 · Viewed 21.1k times · Source

I am aware of the shorthand for map that looks like:

[1, 2, 3, 4].map(&:to_s)
> ["1", "2", "3", "4"]

I was told this is shorthand for:

[1, 2, 3, 4].map{|i| i.to_s}

This makes perfect sense. My question is this: It seems there should be an easier way to write:

[1, 2, 3, 4].map{|x| f.call(x)} 

for some procedure f. I know the way I just typed isn't all that long to begin with, but I'd contend that neither is the previous example for which the shorthand exists. This example just seems like the complement to the first example: Rather than calling i's to_s method for every i, I wish to call f for every x.

Does such a shorthand exist?

Answer

coreyward picture coreyward · Jan 9, 2012

Unfortunately this shorthand notation (which calls "Symbol#to_proc") does not have a way to pass arguments to the method or block being called, so you couldn't even do the following:

array_of_strings.map(&:include, 'l') #=> this would fail

BUT, you are in luck because you don't actually need this shortcut to do what you are trying to do. The ampersand can convert a Proc or Lambda into a block, and vice-versa:

my_routine = Proc.new { |str| str.upcase }
%w{ one two three }.map &my_routine #=> ['ONE', 'TWO', 'THREE']

Note the lack of the colon before my_routine. This is because we don't want to convert the :my_routine symbol into a proc by finding the method and calling .method on it, rather we want to convert the my_routine Proc into a block and pass it to map.

Knowing this, you can even map a native Ruby method:

%w{ one two three }.map &method(:p)

The method method would take the p method and convert it into a Proc, and the & converts it into a block that gets passed to map. As a result, each item gets printed. This is the equivalent of this:

%w{ one two three }.map { |s| p s }