What Does the "Map" Method Do in Ruby

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

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

How to understand Ruby's .each and .map

The map method takes an enum given some block, and iterates through it doing some logic. In your case the logic is x+1. As you say it will not mutate anything unless you use !.

each is simply returning the array that is being called.

Let's take an example of:

names = ["bob"]

If we do:

names.each{|names| names + "somestring"}

the output is still ["bob"]. The reason your second example is different is due to the puts.

As an exercise try doing:

y = [1,2,3].each {|x| puts x + 1}

You will get:

2
3
4
[1,2,3]

In Ruby, what is .map method with rand?

code = (0...16).map { rand(10).to_s }.join

Is used to generate the random string which contains (0 to 9) of length 16. .map will return an array with length of number of times it is iterating (here 16 times). .join is used to join all this 16 elements & return a string

2.2.4 :004 > code = (0...16).map {rand(10).to_s}
=> ["9", "2", "7", "4", "8", "2", "2", "9", "0", "8", "5", "1", "3", "2", "7", "8"]
2.2.4 :005 > code = (0...16).map {rand(10).to_s}.join
=> "8371524929956722"
2.2.4 :006 > code = (0...16).map {rand(10).to_s}.join
=> "4111661481960314"
2.2.4 :007 >

you can use following as well to generate the same result

code = rand(10 ** 16).to_s
=> "7066995392533658"
2.2.4 :011 > code = rand(10 ** 16).to_s
=> "9149902415985481"

Ruby Map Method edits the original array?

Think of using:

  • map as saying "I want to create new data based on existing data"
  • each as saying "I either want to not change any data, or change existing data"

Having this in mind, what you are doing is using map with array to create new array based on existing one, and then using each to modify characters in existing strings. This is why the strings in the original array end up modified.

To fix this use map twice, first to "create new array based on existing array", and then the second time to "create new string based on existing string". This way the original strings won't get modified.

def removal(arr)
letters ="i"
p arr
new_array = arr.map do |word|
word.chars.map do |char|
letters.include?(char) ? '*' : char
end.join
end
p arr
p new_array
end

removal(["hiiiiiigh","git", "training"]) #=> ["hiiiiiigh", "git", "training"]
# ["hiiiiiigh", "git", "training"]
# ["h******gh", "g*t", "tra*n*ng"]

More practical solution to this problem would be something like this:

def censor(strings, forbidden_chars_string, censor_char = '*')
re = Regexp.union(forbidden_chars_string.chars)
strings.map {|str| str.gsub(re, censor_char) }
end

p ["hiiiiiigh","git", "training"] #=> ["hiiiiiigh", "git", "training"]
p censor(["hiiiiiigh","git", "training"], "i") #=> ["h******gh", "g*t", "tra*n*ng"]
p censor(["hiiiiiigh","git", "training"], "gn", '_') #=> ["hiiiiii_h", "_it", "trai_i__"]

What does map(&:name) do in this Ruby code?

events.map(&:name)

is exactly equivalent to

events.map{|x| x.name}

it is just convenient syntactic sugar.

For more details, check out the Symbol#to_proc method here. Here, :name is being coerced to a proc.

By the way, this comes up often here - it's just very hard to google or otherwise search for 'the colon thing with an ampersand' :).

What is the use of map method in ruby?

In your example, when you want to print each value individually right away, each is better. map is useful when you want to save the new versions your elements back into an array, and use that data later in your program.

For instance, say you wanted to forget whether the names had ever been lower or uppercase. Everywhere in your program, the names will be uppercase, so you just want to convert them to uppercase now and be done with that.

a = ["kevin","john","ryan"]
a = a.map{ |n| n.upcase }

Compare that to using each – you would have to use each_with_index, actually, since you need a way to assign the new values back:

a = ["kevin","john","ryan"]
a.each_with_index do |n, i|
a[i] = n.upcase
end

This is the kind of situation map is for. Getting a new Enumerable with changed values, so that you can use the new values later.

By the way, as a shortcut, if you’re assigning a mapped variable back to itself, instead of a = a.map{}, you can use a.map!{}.

Another situation is if you wanted to print all names with an even number of letters. If you used each, here’s how it would look:

a = ["kevin","john","ryan"]
a.each do |n|
if n.size.even?
puts n.upcase
end
end

If you used map, that would give you an array back. Then you could pass that array to select, and finally use each to print the values. This separates the steps more clearly for anyone reading the code, and makes your code more modular if you want to extend it later.

a = ["kevin","john","ryan"]
a.map(&:upcase) \
.select{ |n| n.size.even? } \
.each{ |n| puts n }

The last line could also be this:

 .each(&Kernel.method(:puts))

but that is probably unnecessarily confusing for such a small bit of code. What that does is convert the global puts method into a block using the & operator. Kernel holds all global methods, and .method is the way you get a variable with a method from a class. So .each(&Kernel.method(:puts)) says “get the global puts method, and turn it into a block so that puts is called with the block arguments as parameters”. But in this case, it’s clearer and simpler to write .each{ |n| puts n }.

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:
Ruby Map Method Diagram

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



Related Topics



Leave a reply



Submit