can someone please explain to me how Enum.reduce works with maps
Using Enum.reduce
ultimately delegates to the protocol function Enumerable.reduce
, which is implemented for Map
here:
This first converts the Map
to a list of {key, value}
tuples, then reduces over it.
it would be nice with an example that i can relate with
alphabet_positions = {"a" => 1, "b" => 2, "c" => 3, ...}
Enum.reduce(alphabet_positions, 0, fn({letter, number}, accumulator) ->
if "aeiou" =~ letter do
accumulator += number
end
accumulator
end)
This trivial example adds up the positions of all the vowels in the alphabet.
Maps implement the Enumerable
protocol. That implementation turns the map into a list of key-value tuples - see Map.to_list/1
(the actual implementation uses the Erlang version).
What is being reduced is the resulting list of key-value tuples, not the map itself - which goes something like this:
iex(1)> sum_values = fn {_k, v}, sum -> v + sum end
#Function<13.91303403/2 in :erl_eval.expr/5>
iex(2)> collect_keys = fn {k, _v}, others -> [k | others] end
#Function<13.91303403/2 in :erl_eval.expr/5>
iex(3)> do_both = fn {k, v}, {keys, sum} -> {[k | keys], v + sum} end
#Function<13.91303403/2 in :erl_eval.expr/5>
iex(4)> map = %{"a" => 1, "b" => 2}
%{"a" => 1, "b" => 2}
iex(5)> keyword_list = Map.to_list(map)
[{"a", 1}, {"b", 2}]
iex(6)> values_result = List.foldl(keyword_list, 0, sum_values)
3
iex(7)> keys_result = List.foldl(keyword_list, [], collect_keys)
["b", "a"]
iex(8)> result = List.foldl(keyword_list, {[],0}, do_both)
{["b", "a"], 3}
iex(9)>
Edit: Correction as indicated by @NobbZ
That won’t work, there is no +=
in elixir. I think what you really mean is more like this:
alphabet_positions = %{"a" => 1, "b" => 2, "c" => 3, …}
Enum.reduce(alphabet_positions, 0, fn {letter, position}, sum ->
if letter in ~w[a e i o u], do: sum + position, else: sum
end)
No, its turned into a list of tuples with the key as the first element and the corresponding values as the second. The implementation of Map.to_list/1
is equivalent to this:
map
|> Map.keys()
|> Enum.reduce([], fn key, list -> [{key, map[key]} | list] end)
Looks like it uses an internal iterator to avoid redundant traversals:
Have tried this and the result is true
which can also be implented this way:
Enum.reduce(%{“a” => 1, “b” => 2}, [], fn {k, v}, acc -> [{k, v} | acc] end)
[{“b”, 2}, {“a”, 1}]
your example returns the accumalator of the last vowel which i understand it will be the initial position of the last vowel added to accumulator giving you 6
Going the other way (list to map) Map.new/2
can be helpful.
iex(1)> square_tuple = fn x -> {x , x * x} end
#Function<7.91303403/1 in :erl_eval.expr/5>
iex(2)> result = Map.new(1..10, square_tuple)
%{
1 => 1,
2 => 4,
3 => 9,
4 => 16,
5 => 25,
6 => 36,
7 => 49,
8 => 64,
9 => 81,
10 => 100
}
iex(3)>
accumulator += number
this is a syntax error in elixir
You’re right. I’ve been writing too much JavaScript recently.
Tangentially related, I love the StackOverflow Axiom at play here: “It’s easier to get a good answer by proposing an incorrect implementation than by asking a well-stated question.”