Enum.reduce(enumerable, acc
umulator, fun
ction)
enumerable
is our list ->["a", "a", "a", "b", "c", "c"]
acc
umulator is an empty map ->%{}
acc
will be returned to the function every time the function runsacc
can be any enumerable [List, Map, Range, etc] and can be preloaded if desired
fun
ction will be the action items from above
# starter list
["a", "a", "a", "b", "c", "c"]
# desired result
%{"a" => 3, "b" => 1, "c" => 2}
- Take in a list
- Return a map
- Loop over each element in list
- Add letter key to map if it doesn't exist
- If letter exists, increment its value
Enum.map/2
- ❌ returns a list
@spec map(t(), (element() -> any())) :: list()
Enum.each
- ❌ returns an
:ok
@spec each(t(), (element() -> any())) :: :ok
- ❌ returns an
Enum.reduce
- ✅ returns a map
@spec reduce(t(), acc(), (element(), acc() -> acc())) :: acc()
iex> my_list = ["a", "a", "a", "b", "c", "c", "a", "a", "b"]
#=> ["a", "a", "a", "b", "c", "c", "a", "a", "b"]
iex> Enum.reduce(my_list, %{}, fn char, map ->
Map.update(map, char, 1, fn v -> v + 1 end)
end)
#=> %{"a" => 5, "b" => 2, "c" => 2}
acc1 = Map.update(%{}, "a", 1, fn v -> v + 1 end)
#=> %{"a" => 1}
acc2 = Map.update(acc1, "a", 1, fn v -> v + 1 end)
#=> %{"a" => 2}
acc3 = Map.update(acc2, "a", 1, fn v -> v + 1 end)
#=> %{"a" => 3}
acc4 = Map.update(acc3, "b", 1, fn v -> v + 1 end)
#=> %{"a" => 3, "b" => 1}
acc5 = Map.update(acc4, "c", 1, fn v -> v + 1 end)
#=> %{"a" => 3, "b" => 1, "c" => 1}
acc6 = Map.update(acc5, "c", 1, fn v -> v + 1 end)
#=> %{"a" => 3, "b" => 1, "c" => 2}
acc7 = Map.update(acc6, "a", 1, fn v -> v + 1 end)
#=> %{"a" => 4, "b" => 1, "c" => 2}
acc8 = Map.update(acc7, "a", 1, fn v -> v + 1 end)
#=> %{"a" => 5, "b" => 1, "c" => 2}
acc9 = Map.update(acc8, "b", 1, fn v -> v + 1 end)
#=> %{"a" => 5, "b" => 2, "c" => 2}
Enum.reduce(enumerable, fun
ction)
- tl;dr -> if you want to specify an accumulator, use
reduce/3
instead reduce/2
differs fromreduce/3
in that the first element of theenumerable
is used as the accumulator
Spoiler: this is a shortcut for our Enum.reduce/3
example
@spec frequencies(t) :: map
def frequencies(enumerable) do
reduce(enumerable, %{}, fn key, acc ->
case acc do
%{^key => value} -> %{acc | key => value + 1}
%{} -> Map.put(acc, key, 1)
end
end)
end
iex> my_list = ["a", "a", "a", "b", "c", "c", "a", "a", "b"]
#=> ["a", "a", "a", "b", "c", "c", "a", "a", "b"]
iex> Enum.frequencies(my_list)
#=> %{"a" => 5, "b" => 2, "c" => 2}
The following Enum functions use reduce under the hood 🔧 (53/112)
all?
any?
chunk_while
count
count_until
dedup
dedup_by
drop
drop_every
drop_while
each
empty?
filter
filter_map
find
find_index
find_value
flat_map
flat_map_reduce
frequencies
frequencies_by
group_by
intersperse
into
join
map
map_every
map_intersperse
map_reduce
member?
min_max
min_max_by
split_with
reduce_while
reject
reverse
scan
shuffle
sort
split
split_while
sum
product
take
take_every
take_random
take_while
uniq_by
unzip
with_index
zip
zip_with
zip_reduce
On top of that, tons of private helper functions within Enum use reduce as well 🤯