100
hash = { "d" => [11, 22], "f" => [33, 44, 55] }

# case 1
hash.map {|k,vs| vs.map {|v| "#{k}:#{v}"}}.join(",")
=> "d:11,d:22,f:33,f:44,f:55"

# case 2
hash.map {|k,vs| vs.each {|v| "#{k}:#{v}"}}.join(",")
=> "11,22,33,44,55"

only difference is case 1 uses vs.map, case 2 uses vs.each.

What happened here?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
user612308
  • 1,813
  • 5
  • 23
  • 22

7 Answers7

194

Array#each executes the given block for each element of the array, then returns the array itself.

Array#map also executes the given block for each element of the array, but returns a new array whose values are the return values of each iteration of the block.

Example: assume you have an array defined thusly:

arr = ["tokyo", "london", "rio"]

Then try executing each:

arr.each { |element| element.capitalize }
# => ["tokyo", "london", "rio"]

Note the return value is simply the same array. The code inside the each block gets executed, but the calculated values are not returned; and as the code has no side effects, this example performs no useful work.

In contrast, calling the array's map method returns a new array whose elements are the return values of each round of executing the map block:

arr.map { |element| element.capitalize }
# => ["Tokyo", "London", "Rio"]
Teemu Leisti
  • 3,750
  • 2
  • 30
  • 39
Kyle d'Oliveira
  • 6,382
  • 1
  • 27
  • 33
  • 1
    Perfect answer to understand. Just a.. Disclaimer: If you overuse the return value of the map function you can potentially waste a lot of memory. – Imran Ahmad Feb 26 '17 at 08:02
35

The side effects are the same which is adding some confusion to your reverse engineering.

Yes, both iterate over the array (actually, over anything that mixes in Enumerable) but map will return an Array composed of the block results while each will just return the original Array.

The return value of each is just the original array and is rarely used in Ruby code but map is one of the most important functional tools.

What map does is return an array which contains the results of the block or named method that is passed. For example:

    2.2.3 :001 > [:how, :now, :brown, :cow].map &:to_s
 => ["how", "now", "brown", "cow"]

In this case I didn't pass a block but just a Symbol, however class Symbol objects have a to_proc method which will result in:

[:how.to_s, :now.to_s, ...]

BTW, you may be having a hard time finding the documentation because map is a method in Enumerable while each (the one method required by the Enumerable module) is a method in Array.

As a trivia note: the map implementation is based on each.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
14

Here's a quick demo of how map differs from each

a = ["a", "b", "c"];
#Array.map
p a.map {|item| "map_" + item}
#prints ["map_a", "map_b", "map_c"]

#Array.each
p a.each {|item| "map_" + item}
#prints ["a", "b", "c"]

You see that map returns ["map_a", "map_b", "map_c"] whereas each just iterates but returns the original array: ["a", "b", "c"].

So each is used for processing an array and map is used to do something with a processed array.

Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
Rima
  • 1,781
  • 14
  • 12
4

.each returns the same array you provided initially:

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

.map returns a new Array out of the results of each block call:

[1,2,3].map { |i| i + 1 }
#=> [2,3,4]
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
pastullo
  • 4,171
  • 3
  • 30
  • 36
1

Array#each method returns same array

a = [1,2,3,4,5]
a.object_id #70284994490700

b = a.each {|n| n + 2}
p b #[1,2,3,4,5]
b.object_id #70284994490700 <<--- it's the same as a

Array#map method returns a new array

c = [1,2,3,4,5]
c.object_id #70219117705860

d = c.map {|n| n + 2}
p d #[3,4,5,6,7]
d.object_id #70284994343620  <<---- it's different than c
user3007294
  • 931
  • 1
  • 14
  • 33
0

when you use map to a hash, it implicitly casts the hash to an array, so you have

[["d", [11, 22]], ["f", [33, 44, 55]]]

vs.each{...} only gives you back the last evaluation, which is [11, 22] for ["d", [11, 22]] and [33, 44, 55] for ["f", [33, 44, 55]]. So before the last join, you have

[[11, 22], [33, 44, 55]]
sawa
  • 165,429
  • 45
  • 277
  • 381
0

You could also to use map with bang map! that change source array

  • This is not an answer to the question. the question is about the difference between `each`, which executes the given block and returns the original array; and `map`, which returns an array with the result of executing the block as values – Sampson Crowley Mar 26 '20 at 19:44