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
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 the map method do in Ruby?
The map
method takes an enumerable object and a block, and runs the block for each element, outputting each returned value from the block (the original object is unchanged unless you use map!)
:
[1, 2, 3].map { |n| n * n } #=> [1, 4, 9]
Array
and Range
are enumerable types. map
with a block returns an Array. map!
mutates the original array.
Where is this helpful, and what is the difference between map!
and each
? Here is an example:
names = ['danil', 'edmund']
# here we map one array to another, convert each element by some rule
names.map! {|name| name.capitalize } # now names contains ['Danil', 'Edmund']
names.each { |name| puts name + ' is a programmer' } # here we just do something with each element
The output:
Danil is a programmer
Edmund is a programmer
Emulating the each and map method without using each and map. Explanation on the mechanics of the code needed
I think your explanation is correct and you do have the right idea.
The &proc
and proc.call
syntax is fine, but you could alternately write it using yield
like so:
def my_each
i = 0
while i < self.count
yield self[i]
i += 1
end
self
end
def my_map
result = []
my_each { |el| result << yield(el) }
result
end
Should this use the map method?
Whether to use map
or each
depends totally on whether or not you want the operation to return a list of the results of the application of the method (the send
) or not.
`return` in Ruby Array#map
You don't need the variable. Return value of the block is value of last expression evaluated in it. In this case, the if
.
def some_method(array)
array.map do |x|
if x > 10
x+1
else
x-1
end
end
end
Ternary operator would look nicer, I think. More expression-ish.
def some_method(array)
array.map do |x|
(x > 10) ? x+1 : x-1
end
end
If you insist on using return
, then you could use lambdas. In lambdas, return
behaves like in normal methods.
def some_method(array)
logic = ->(x) {
if x > 10
return x + 1
else
return x - 1
end
}
array.map(&logic)
end
This form is rarely seen, though. If your code is short, it surely can be rewritten as expressions. If your code is long and complicated enough to warrant multiple exit points, then probably you should try simplifying it.
In Ruby, is there an Array method that combines 'select' and 'map'?
I usually use map
and compact
together along with my selection criteria as a postfix if
. compact
gets rid of the nils.
jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}
=> [3, 3, 3, nil, nil, nil]
jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact
=> [3, 3, 3]
What is map(&:id) in Rails?
1 )
You should read some tutorials on map
to get acquainted.
https://www.rubyguides.com/2018/10/ruby-map-method
But the short answer is that running user.cnae_classifications.map(&:id)
will loop over all cnae_classifications
and extract the id
from them and put them all into an array. The &
character allows for map
shorthand to avoid passing an entire block.
From the link above:
2 )
The #create
method can accept a key-value hash of known attributes (known to the class in question, in this case that is UserCnaeClassification
) to assign upon creation. So you're basically right, they are key-value pairs but they are specific to this class/object. Those same keys might not work on another class/object.
Additional reading: https://guides.rubyonrails.org/active_record_basics.html#create
Passing custom method from custom class to .map
First of all: you can use array.sample
instead of array[rand(x)]
to get a random element from an array.
Furthermore, you can replace (1..n).map
by n.times.map
:
(2.times.map { rand_letters } + 3.times.map { rand_numbers }).join
But using map
feels counter intuitive to me. Wouldn't it be much easier if we could simply grab 3 random letters?
This can be achieved by returning an Enumerator
instead of a single value. The plural naming already suggest that the rand_
methods are supposed to return multiple values:
def rand_letters
return enum_for(__method__) unless block_given?
loop { yield ('A'..'Z').to_a.sample }
end
def rand_numbers
return enum_for(__method__) unless block_given?
loop { yield (0..9).to_a.sample }
end
This allows us to call Enumerable#take
:
(rand_letters.take(2) + rand_numbers.take(3)).join
#=> "ZJ010"
Or using the [...]
literal:
[rand_letters.take(2), rand_numbers.take(3)].join
Related Topics
How to Override Gemfile for Local Development
Generate Random String Based on Regex
Rails - How to Check for Online Users
How to Specify a Read Timeout for a Net::Http::Post.New Request in Ruby 2
How to Convert Utf8 Combined Characters into Single Utf8 Characters in Ruby
What Do 'Def +@' and 'Def -@' Mean
Ruby: Yield Block from a Block
Execjs::Programerror in Welcome#Index Typeerror: Object Doesn't Support This Property or Method
Ruby: How to Write a Bang Method, Like Map
How to Search Array Through Ransack Gem
Multi Level Block Method Is Generating Issue
Ruby on Rails Activerecord Scopes VS Class Methods
Undefined Method Respond_To in Rails 5 Controller
Unicode Characters in a Ruby Script
How to Find All Modules and Classes Within a Module, Recursively