Skip to content

snelson82/enum_deep_dive

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Enum.reduce(enumerable, accumulator, function)

  • enumerable is our list -> ["a", "a", "a", "b", "c", "c"]
  • accumulator is an empty map -> %{}
    • acc will be returned to the function every time the function runs
    • acc can be any enumerable [List, Map, Range, etc] and can be preloaded if desired
  • function will be the action items from above

Example Problem

# starter list
["a", "a", "a", "b", "c", "c"]

# desired result
%{"a" => 3, "b" => 1, "c" => 2}

Pseudo spec 🧠

  • 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

Options?

  • Enum.map/2
    • ❌ returns a list
    • @spec map(t(), (element() -> any())) :: list()
  • Enum.each
    • ❌ returns an :ok
    • @spec each(t(), (element() -> any())) :: :ok
  • 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}

Results, step by step with verbosity

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/3 instead
  • reduce/2 differs from reduce/3 in that the first element of the enumerable 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}

reduce_all_the_things

reduce_always_has_been

The following Enum functions use reduce under the hood 🔧 (53/112)

  1. all?
  2. any?
  3. chunk_while
  4. count
  5. count_until
  6. dedup
  7. dedup_by
  8. drop
  9. drop_every
  10. drop_while
  11. each
  12. empty?
  13. filter
  14. filter_map
  15. find
  16. find_index
  17. find_value
  18. flat_map
  19. flat_map_reduce
  20. frequencies
  21. frequencies_by
  22. group_by
  23. intersperse
  24. into
  25. join
  26. map
  27. map_every
  28. map_intersperse
  29. map_reduce
  30. member?
  31. min_max
  32. min_max_by
  33. split_with
  34. reduce_while
  35. reject
  36. reverse
  37. scan
  38. shuffle
  39. sort
  40. split
  41. split_while
  42. sum
  43. product
  44. take
  45. take_every
  46. take_random
  47. take_while
  48. uniq_by
  49. unzip
  50. with_index
  51. zip
  52. zip_with
  53. zip_reduce

On top of that, tons of private helper functions within Enum use reduce as well 🤯

About

Studies for Elixir's Enum module

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published