How to Use the Ruby Map Method

Jul 29, 2020 - 4 min read
How to Use the Ruby Map Method
Share

I think it's so cool that the Ruby map method exists. It means that Ruby has support for higher-order functions, and functional programming.

Here's the definition of higher-order functions.

Higher-order functions are functions that accept a function as an argument and/or return a function as a return value.

How does the map method work

The way the map method works in Ruby is, it takes an enumerable object, (i.e. the object you call it on), and a block.

Then, for each of the elements in the enumerable, it executes the block, passing it the current element as an argument. The result of evaluating the block is then used to construct the resulting array.

In other words:

Applying map on an array returns a new array where each element is the result of evaluating the block with the element as an argument.

As you can see, the block plays the role of the function in Ruby. It's actually a function object (or a functor), but that's just a side note.

An example might make it easier to understand.

[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]

In this example, the block (i.e. { |n| n * 2 }) is applied to each element of the initial array (i.e. [1, 2, 3]) in turn, resulting in a new array.

What does map(&:name) mean in Ruby?

It's a shorthand notation, and it's useful when all you need to do inside the block is call a method.

[1, 2, 3].map { |n| n.even? }

# could be written as

[1, 2, 3].map(&:even?)

If you want to know how this works, take a look at What does &block (ampersand parameter) mean?.

Map over a Hash

There's a new method (since Ruby 2.4) called Hash#transform_values that you could use pretty much like you would use an Array.

h = { a: 1, b: 2, c: 3 }

h.transform_values {|v| v * v + 1 }
# => { a: 2, b: 5, c: 10 }

h.transform_values(&:to_s)
# => { a: "1", b: "2", c: "3" }

Map over a nested Array

If you have a 2D array (a matrix) and you want to map over it, you would do it like this.

my_array = [
  [1, 2, 3, 4],
  [5, 6, 7, 8],
]

my_array.map { |row| row.map { |col| col + 1 } }
# => [[2, 3, 4, 5], [6, 7, 8, 9]]

It will return a new array. If you want to change the values of the initial array, you can use map!.

Map and remove nil values

The resulting array could contain a few nil values, and you might want to get rid of them. Here are a few ways you could do it.

[1, 2, 3].map { |n| n == 2 ? n : nil } # => [nil, 2, nil]

[1, 2, 3].select { |n| n == 2 ? n : nil } # => [2]
[1, 2, 3].map { |n| n == 2 ? n : nil }.compact # => [2]
[1, 2, 3].map { |n| n == 2 ? n : nil } - [nil] # => [2]
[1, 2, 3].map { |n| n == 2 ? n : nil }.reject(&:nil?) # => [2]
[1, 2, 3].reduce([]) do |memo, n|
  memo << n if n == 2
  memo
end

How to map with an index

Sometimes you need an index. Maybe you want to append the index to the value. Or maybe you have a better reason :)

["a", "b", "c"].each_with_index.map { |n, i| n + i.to_s } # => ["a0", "b1", "c2"]
["a", "b", "c"].map.with_index { |n, i| n + i.to_s } # => ["a0", "b1", "c2"]

Map vs. each

You use map to collect the result of running the block over the elements of the array. And you use each to run the block over the elements without collecting the values. You can read more about using the each method here.

Map vs. collect

They are aliases for each other, so there is no difference. But map is a more popular name for what it's doing than collect is.

Map vs. select

select is different than the ruby map method. What select does is it returns all the objects for which the block returns a truthy value (i.e. not nil or false).

12 Project Ideas
Cezar Halmagean
Software development consultant with over a decade of experience in helping growing companies scale large Ruby on Rails applications. Has written about the process of building Ruby on Rails applications in RubyWeekly, SemaphoreCI, and Foundr.