For, Map and Reduce in Elixir
Tags: elixir, beginners
Published: 2023-11-02 19:32:45

# Introduction

[![Run in Livebook](https://livebook.dev/badge/v1/pink.svg)](https://livebook.dev/run?url=https://gist.github.com/adolfont/07e547a3cc3026035f26f6be6e255cb4)

<!-- before ChatGPT
A student of my [Introduction to Functional Programming](https://github.com/adolfont/caes005-introduction-to-functional-programming) course wrote a piece of code where it was clear to me that he was thinking that the Elixir's for behaves in a similar way to 'for' commands in other non-functional programming languages. But it does not.
--> 

One of the students in my [Introduction to Functional Programming](https://github.com/adolfont/caes005-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.

```elixir
for i <- 1..10 do
  i * 10
end
```

<!-- livebook:{"output":true} -->

```
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
```

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

```elixir
1..10
|> Enum.map(fn x -> x * 10 end)
```

<!-- livebook:{"output":true} -->

```
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
```

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.

```elixir
1..10
|> Enum.map(fn x -> x * 10 end)
|> Enum.sum()
```

<!-- livebook:{"output":true} -->

```
550
```

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

```elixir
1..10
|> Enum.reduce(fn x, accum -> x * 10 + accum end)
```

<!-- livebook:{"output":true} -->

```
541
```

The result is different. Why?


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

```elixir
1..10
|> Enum.reduce(fn x, accum -> x * 10 * accum end)
```

<!-- livebook:{"output":true} -->

```
3628800000000000
```

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

```elixir
10 * 20 * 30 * 40 * 50 * 60 * 70 * 80 * 90 * 100
```

<!-- livebook:{"output":true} -->

```
36288000000000000
```

The correct way is:

```elixir
1..10
|> Enum.reduce(1, fn x, accum -> x * 10 * accum end)
```

<!-- livebook:{"output":true} -->

```
36288000000000000
```

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):

```elixir
for i <- 1..3, j <- ["Brasil", "Mexico", "Angola"] do
  {:number, i, :country, j}
end
```

<!-- livebook:{"output":true} -->

```
[
  {: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"}
]
```

You can also add filters:

```elixir
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
```

<!-- livebook:{"output":true} -->

```
[{:number, 2, :country, "Brasil"}]
```

There are many more things that you can do with 'for', 'map' and 'reduce'. Explore Elixir's docs ([for](https://hexdocs.pm/elixir/1.16/comprehensions.html), [Enum](https://elixirschool.com/pt/lessons/basics/enum))to learn more!