Enum.reduce(enumerable, accumulator, function)
enumerableis our list ->["a", "a", "a", "b", "c", "c"]accumulator is an empty map ->%{}accwill be returned to the function every time the function runsacccan be any enumerable [List, Map, Range, etc] and can be preloaded if desired
function 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, function)
- tl;dr -> if you want to specify an accumulator, use
reduce/3instead reduce/2differs fromreduce/3in that the first element of theenumerableis 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)
endiex> 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_whilecountcount_untildedupdedup_bydropdrop_everydrop_whileeachempty?filterfilter_mapfindfind_indexfind_valueflat_mapflat_map_reducefrequenciesfrequencies_bygroup_byintersperseintojoinmapmap_everymap_interspersemap_reducemember?min_maxmin_max_bysplit_withreduce_whilerejectreversescanshufflesortsplitsplit_whilesumproducttaketake_everytake_randomtake_whileuniq_byunzipwith_indexzipzip_withzip_reduce
On top of that, tons of private helper functions within Enum use reduce as well 🤯

