# Deep understanding of Enum.map_reduce/3 in Elixir

Today I found a function in Elixir’s standard lib that I have often needed.

Introducing: `Enum.map_reduce/3`

`Enum.map_reduce/3` can replace `Enum.reduce/3` when the `reduce` maps each element to another element and we also want to maintain some state or build a result along the way.

This might be a bit cryptic, so let’s start with an example.

## Have a cache while mapping

Let’s pretend I don’t know about `Enum.map_reduce/3` and I need to map a list of numbers to the maximum of the number and the previous number.

I would do something like this:

``````input = [5, 2, 8, 9, 1, 2, 7, 2]

{_last, numbers} =
Enum.reduce(input, {0, []}, fn num, {last, acc} ->
{num, [max(num, last) | acc]}
end)

Enum.reverse(numbers)
``````

(Ok, this simple example could also be solved nicely with `Enum.chunk_every/2` and `Enum.map/2`, but for the sake of example let’s play with this)

This will give the expected result `[5, 5, 8, 9, 9, 2, 7, 7]`.

Notice that I need to reverse the number list at the end, since I append numbers to the start of the list.

If only there is a way to do this in a smarter way…

Well, with `Enum.map_reduce/3` we can do this:

``````input = [5, 2, 8, 9, 1, 2, 7, 2]

{numbers, _last} =
Enum.map_reduce(input, 0, fn num, last -> {max(num, last), num} end)
``````

So here we use `map_reduce`‘s accumulator to remember what the last number was.

## Build a result while mapping

We can also use it to accumulate something we need as a result while we’re mapping a list.

Perhaps we want to know the largest number while doing the max-thing above:

``````{numbers, {max, _last}} =
Enum.map_reduce(input, {0, 0}, fn num, {max, last} ->
{max(num, last), {max(num, max), num}}
end)
``````

Which will give the same `numbers` as before, but now we also get `9` as the max item.

## Enum.flat_map_reduce

In the cases where each iteration might yield zero or many items to the resulting list, we can use `Enum.flat_map_reduce/3`. This works like `Enum.map_reduce/3` with two differences:

• Each iteration must yield a list of elements instead of a single element. Notice that we can use an empty list if an iteration should not add something to the resulting list.
• It is possible to break early using `:halt`, almost like with `Enum.reduce_while/3`

Example: We want to map a list to another list where:

• All odd numbers are removed
• All even numbers are doubled
• We want to sum the original numbers
• If we hit 7, we stop
``````input = [5, 2, 8, 9, 1, 2, 7, 2]

Enum.flat_map_reduce(input, 0, fn num, sum ->
case num do
7 -> {:halt, sum}
n when rem(n, 2) == 0 -> {[n, n], sum + n}
n -> {[], sum + n}
end
end)
``````

Which will give `{[2, 2, 8, 8, 2, 2], 27}` 