DEV Community

Cover image for For, Map and Reduce in Elixir
Adolfo Neto
Adolfo Neto

Posted on

For, Map and Reduce in Elixir

Introduction

Run in Livebook

One of the students in my Introduction to Functional Programming course recently submitted a code snippet. It became evident that they assumed Elixir's 'for' construct operates similarly to 'for' loops in non-functional programming languages. However, this is not the case, as Elixir's 'for' is fundamentally different in its behavior.

What's a list comprehension?

The command 'for' in Elixir is a list comprehension. The result of a 'for' is a list.

For instance, in the example below, 'i' goes from one to ten. The result is a list containing each value of 'i' multiplied by 10.

for i <- 1..10 do
  i * 10
end
Enter fullscreen mode Exit fullscreen mode
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Enter fullscreen mode Exit fullscreen mode

You could do the same using Enum.map/2.

1..10
|> Enum.map(fn x -> x * 10 end)
Enter fullscreen mode Exit fullscreen mode
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Enter fullscreen mode Exit fullscreen mode

If, instead, you wanted the result of the sum of all the values of the list, you would have two options.

The first one is to use Enum.sum() to sum all values of the resulting list.

1..10
|> Enum.map(fn x -> x * 10 end)
|> Enum.sum()
Enter fullscreen mode Exit fullscreen mode
550
Enter fullscreen mode Exit fullscreen mode

The second option is to use Enum.reduce/2:

1..10
|> Enum.reduce(fn x, accum -> x * 10 + accum end)
Enter fullscreen mode Exit fullscreen mode
541
Enter fullscreen mode Exit fullscreen mode

The result is different. Why?

What if I wanted to multiply all values of the resulting list? The following solution would not work.

1..10
|> Enum.reduce(fn x, accum -> x * 10 * accum end)
Enter fullscreen mode Exit fullscreen mode
3628800000000000
Enter fullscreen mode Exit fullscreen mode

Because this is the value of 10 *... * 100:

10 * 20 * 30 * 40 * 50 * 60 * 70 * 80 * 90 * 100
Enter fullscreen mode Exit fullscreen mode
36288000000000000
Enter fullscreen mode Exit fullscreen mode

The correct way is:

1..10
|> Enum.reduce(1, fn x, accum -> x * 10 * accum end)
Enter fullscreen mode Exit fullscreen mode
36288000000000000
Enter fullscreen mode Exit fullscreen mode

What's the difference between Enum.reduce/2 and Enum.reduce/3?

Back to for

'For' allows you to have more than one generator (the 'i <- 1..10' part):

for i <- 1..3, j <- ["Brasil", "Mexico", "Angola"] do
  {:number, i, :country, j}
end
Enter fullscreen mode Exit fullscreen mode
[
  {:number, 1, :country, "Brasil"},
  {:number, 1, :country, "Mexico"},
  {:number, 1, :country, "Angola"},
  {:number, 2, :country, "Brasil"},
  {:number, 2, :country, "Mexico"},
  {:number, 2, :country, "Angola"},
  {:number, 3, :country, "Brasil"},
  {:number, 3, :country, "Mexico"},
  {:number, 3, :country, "Angola"}
]
Enter fullscreen mode Exit fullscreen mode

You can also add filters:

require Integer

for i <- 1..3,
    j <- ["Brasil", "Mexico", "Angola"],
    Integer.is_even(i),
    String.starts_with?(j, "B") do
  {:number, i, :country, j}
end
Enter fullscreen mode Exit fullscreen mode
[{:number, 2, :country, "Brasil"}]
Enter fullscreen mode Exit fullscreen mode

There are many more things that you can do with 'for', 'map' and 'reduce'. Explore Elixir's docs (for, Enum)to learn more!

Top comments (0)