bill
bill

Reputation: 368

How to understand Ruby's .each and .map

I am having trouble understanding the differences between map and each, and where and when to use them.

I read "What does map do?" and "Ruby Iterators" but wanted some clarification.

If I have:

 z = [1,2,3].map {|x| x + 1}

map takes each element in the array z and adds one to each element, however it does not mutate the original array unless I add !.

On the other hand:

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

returns [1,2,3]. This is confusing to me since:

names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }

returns:

Danil is a programmer
Edmund is a programmer

What is exactly going on in my second example that isn't allowing each array element to be increased by 1, while in the last example a string is being attached to everything in the array?

All credits go to Speransky Danil, whom I took these examples off of.

Upvotes: 5

Views: 3513

Answers (5)

the Tin Man
the Tin Man

Reputation: 160621

This is covered in Ruby's documentation in multiple places but the easiest to understand for your use is in the Array documentation for each:

each { |item| block } → ary
each → Enumerator

Calls the given block once for each element in self, passing that element as a parameter. Returns the array itself.

If no block is given, an Enumerator is returned.

a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }

produces:

a -- b -- c --

Note that it says "Returns the array itself."

Compare that to map:

map { |item| block } → new_ary
map → Enumerator

Invokes the given block once for each element of self.

Creates a new array containing the values returned by the block.

See also Enumerable#collect.

If no block is given, an Enumerator is returned instead.

a = [ "a", "b", "c", "d" ]
a.collect { |x| x + "!" }         #=> ["a!", "b!", "c!", "d!"]
a.map.with_index { |x, i| x * i } #=> ["", "b", "cc", "ddd"]
a                                 #=> ["a", "b", "c", "d"]

Note that it says "Creates a new array containing the values returned by the block."

This example should help knowing the above:

foo = [1,2,3]
foo.each { |i| puts i + 1 } # => [1, 2, 3]
foo.map { |i| i + 1 } # => [2, 3, 4]

# >> 2
# >> 3
# >> 4

where # => is the return value of the block and # >> is the captured STDOUT from puts.

And, knowing all that, use each when you want to display elements in the array or extract and reuse those elements as parameters or to build things. Use map when you want to change the elements of the array into something else.

Upvotes: 1

Motombo
Motombo

Reputation: 1787

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]

Upvotes: 6

iamsolankiamit
iamsolankiamit

Reputation: 37

In the first case, map:

 z = [1,2,3].map {|x| x + 1}

will take each element in the given array and perform the operation in the block and return a new array, so here it returns [2,3,4].

.each executes the block for each of the elements in the array, and it will not change anything in the array, so here it performs x + 1, but it doesn't store it anywhere, hence in the second case it just returns the array.

Now in the third example you posted, you are printing output in the block itself. Again, there is no change in the array itself.

Upvotes: 0

Ursus
Ursus

Reputation: 30071

tl;dr: I use map if I want to change my collection, apply a transformation on it, end up with something different. I use each if I just need to visit every element in a collection.

Key point is: you should use map if you want to apply a transformation on an array (an enumerable in reality, but let's keep it simple at the beginning). Otherwise, if you don't need to change your array, you can simply use each.

Note that in the code below you are not mutating the array but you are simply take advantage of the local string to print each string with a suffix.

names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }

Obviously, you could do the same with map but in this case you don't need it and you have to use an each too to print every element. The code would be

names = ['danil', 'edmund']
names.map! { |name| name + ' is a programmer' }
# or names = names.map { |name| name + ' is a programmer' }
name.each { |name| puts name }

Upvotes: 3

schubam
schubam

Reputation: 46

The difference is that each is performing an action on each element in the array, returning the original array. The action performed possibly mutated the element.

Whereas map is performing an action on each element in the array and returning its result as an array.

Upvotes: 0

Related Questions