Other Ruby Map Shorthand Notation

Other Ruby Map Shorthand Notation

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 }

Shorthand for functions to map

use this:

nums.each(&method(:puts))

but actually it's not much shorter :)

Can you supply arguments to the map(&:method) syntax in Ruby?

You can create a simple patch on Symbol like this:

class Symbol
def with(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

Which will enable you to do not only this:

a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11]

But also a lot of other cool stuff, like passing multiple parameters:

arr = ["abc", "babc", "great", "fruit"]
arr.map(&:center.with(20, '*'))
# => ["********abc*********", "********babc********", "*******great********", "*******fruit********"]
arr.map(&:[].with(1, 3))
# => ["bc", "abc", "rea", "rui"]
arr.map(&:[].with(/a(.*)/))
# => ["abc", "abc", "at", nil]
arr.map(&:[].with(/a(.*)/, 1))
# => ["bc", "bc", "t", nil]

And even work with inject, which passes two arguments to the block:

%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde"

Or something super cool as passing [shorthand] blocks to the shorthand block:

[['0', '1'], ['2', '3']].map(&:map.with(&:to_i))
# => [[0, 1], [2, 3]]
[%w(a b), %w(c d)].map(&:inject.with(&:+))
# => ["ab", "cd"]
[(1..5), (6..10)].map(&:map.with(&:*.with(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

Here is a conversation I had with @ArupRakshit explaining it further:

Can you supply arguments to the map(&:method) syntax in Ruby?


As @amcaplan suggested in the comment below, you could create a shorter syntax, if you rename the with method to call. In this case, ruby has a built in shortcut for this special method .().

So you could use the above like this:

class Symbol
def call(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11]

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

Here is a version using Refinements (which is less hacky than globally monkey patching Symbol):

module AmpWithArguments

refine Symbol do
def call(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

end

using AmpWithArguments

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11]

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

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

It's shorthand for tags.map(&:name.to_proc).join(' ')

If foo is an object with a to_proc method, then you can pass it to a method as &foo, which will call foo.to_proc and use that as the method's block.

The Symbol#to_proc method was originally added by ActiveSupport but has been integrated into Ruby 1.8.7. This is its implementation:

class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end

Ruby: map an array of keys to values shorthand

SYMBOLS.values_at(str.upcase.chars.to_a)

Regarding using SYMBOLS[], you'd still need to pass the character to the method.

You can get the method via SYMBOLS.method(:[]), e.g.,

> p = SYMBOLS.method(:[])
> p.call("X")
=> 10

I'm not convinced it's the most readable in this case–for me, calling map and passing in SYMBOLS[], while concise and functional, delays understanding what's happening longer than I prefer.

How do I pass an argument to array.map short cut?

You can't do it like this. The & operator is for turning symbols into procs.

a = [1, 2, 3, 4, 5]  
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.

& is a shorthand for to_proc:

def to_proc
proc { |obj, *args| obj.send(self, *args) }
end

It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.

Chaining methods using Symbol#to_proc shorthand in Ruby?

No, there's no shorthand for that. You could define a method:

def really_empty?(x)
x.strip.empty?
end

and use method:

array.reject(&method(:really_empty?))

or use a lambda:

really_empty = ->(x) { x.strip.empty? }
array.reject(&really_empty)

but I wouldn't call either of those better unless you have a use for really_empty? in enough places that splitting up the logic makes sense.

However, since you're using Rails, you could just use blank? instead of .strip.empty?:

array.reject(&:blank?)

Note that nil.blank? is true whereas nil.strip.empty? just hands you an exception so they're not quite equivalent; however, you probably want to reject nils as well so using blank? might be better anyway. blank? also returns true for false, {}, and [] but you probably don't have those in your array of strings.

ruby using the &:methodname shortcut from array.map(&:methodname) for hash key strings rather than methodname

You can do this with a lambda:

extract_keyname = ->(h) { h[:keyname] }
ary_of_hashes.map(&extract_keyname)

This tends to be more useful if the block's logic is more complicated than simply extracting a value from a Hash. Also, attaching names to your bits of logic can help clarify what a chain of Enumerable method calls is trying to do.

You can also have a lambda which returns a lambda if you're doing this multiple times:

extract = ->(k) { ->(h) { h[k] } }
ary_of_hashes.map(&extract[:keyname])
ary_of_hashes.map(&extract[:other_key])

or a lambda building method:

def extract(k)
->(h) { h[k] }
end

ary_of_hashes.map(&extract(:keyname))
ary_of_hashes.map(&extract(:other_key))

How to use &:symbol notation with map and a custom method?

The & notation that converts a symbol to a proc doesn't work like that. It creates a block where the argument passed to it is the receiver of the method call, not the argument.

p [1,2,3].map(&:add_one)
# is equivalent to this
p [1,2,3].map { |el| el.add_one }

How can I call add_one with the &: notation?

You don't. You simply wouldn't use the shorthand form in this case. There's no reason to.

Is there a shorthand for {|x| foo x}?

You can reference the method to pass as a proc with method:

list.map(&method(:foo))

Maybe not the shortcut you're looking for, as it's limited to work with methods that expect only one argument and reduces readability.



Related Topics



Leave a reply



Submit