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 nil
s 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
How to Format a String with Floats in Ruby Using #{Variable}
How to Get the Version from a Gemspec File
Trouble Resizing the Default Image with Paperclip
How to Recursively Require All Files in a Directory in Ruby
How to Get a HTML Table Row with Capybara
Using Nokogiri HTML Builder to Create Fragment with Multiple Root Nodes
In Ruby, How to I Control the Order in Which Test::Unit Tests Are Run
How to Configure Mongomapper and Activerecord in Same Ruby Rails Project
Deleting While Iterating in Ruby
How to Assign Multiple Values to a Hash Key
Counting Days Excluding Weekends
Help Understanding Yield and Enumerators in Ruby
How to Get the Current Route in Rails
Ruby Way to Group Anagrams in String Array