Elixir Accumulator 地图列表
Elixir Accumulator List of Maps
你能帮我从地图列表中实现一个累加器吗?。
[
%{
score: 1,
name: "Javascript",
},
%{
score: 2,
name: "Elixir",
},
%{
score: 10,
name: "Elixir",
}
]
结果应该是:
[
%{
score: 12,
name: "Elixir",
},
%{
score: 1,
name: "Javascript",
}
]
我会感谢你的建议。
此致
假设您的原始列表存储在 input
局部变量中,可以从 Enum.reduce/3
using Map.update/4
作为缩减器开始。
Enum.reduce(input, %{}, fn %{score: score, name: name}, acc ->
Map.update(acc, name, score, & &1 + score)
end)
#⇒ %{"Elixir" => 12, "Javascript" => 1}
无论你是否坚持要有一个地图列表作为结果(这是可读性较差的 IMSO,)更进一步 Enum.map/2
结果:
Enum.map(%{"Elixir" => 12, "Javascript" => 1}, fn {name, score} ->
%{name: name, score: score}
end)
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
总结一下:
input
|> Enum.reduce(%{}, fn %{score: score, name: name}, acc ->
Map.update(acc, name, score, & &1 + score)
end)
|> Enum.map(& %{name: elem(&1, 0), score: elem(&1, 1)})
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
旁注: maps in erlang (and, hence, in elixir) are not ordered. That means, if you want the resulting list to be sorted by name
, or by score
, you should explicitly Enum.sort/2
it:
Enum.sort(..., & &1.score > &2.score)
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
一种简单的方法是使用 Enum.group_by/3
to group the items by name, then Enum.sum/1
对分数求和:
list
|> Enum.group_by(& &1.name, & &1.score)
|> Enum.map(fn {name, score} -> %{name: name, score: Enum.sum(score)} end)
输出:
[%{name: "Elixir", score: 12}, %{name: "Javascript", score: 1}]
如果您希望创建和使用更通用的解决方案,您可以创建自己的合并模块。
defmodule Merger do
def merge_by(enumerable, name_fun, merge_fun) do
enumerable
|> Enum.group_by(name_fun)
|> Enum.map(fn {_name, items} -> Enum.reduce(items, merge_fun) end)
end
end
list = [
%{score: 1, name: "Javascript"},
%{score: 2, name: "Elixir"},
%{score: 10, name: "Elixir"}
]
Merger.merge_by(list, & &1.name, &%{&1 | score: &1.score + &2.score})
# => [%{name: "Elixir", score: 12}, %{name: "Javascript", score: 1}]
你能帮我从地图列表中实现一个累加器吗?。
[
%{
score: 1,
name: "Javascript",
},
%{
score: 2,
name: "Elixir",
},
%{
score: 10,
name: "Elixir",
}
]
结果应该是:
[
%{
score: 12,
name: "Elixir",
},
%{
score: 1,
name: "Javascript",
}
]
我会感谢你的建议。
此致
假设您的原始列表存储在 input
局部变量中,可以从 Enum.reduce/3
using Map.update/4
作为缩减器开始。
Enum.reduce(input, %{}, fn %{score: score, name: name}, acc ->
Map.update(acc, name, score, & &1 + score)
end)
#⇒ %{"Elixir" => 12, "Javascript" => 1}
无论你是否坚持要有一个地图列表作为结果(这是可读性较差的 IMSO,)更进一步 Enum.map/2
结果:
Enum.map(%{"Elixir" => 12, "Javascript" => 1}, fn {name, score} ->
%{name: name, score: score}
end)
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
总结一下:
input
|> Enum.reduce(%{}, fn %{score: score, name: name}, acc ->
Map.update(acc, name, score, & &1 + score)
end)
|> Enum.map(& %{name: elem(&1, 0), score: elem(&1, 1)})
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
旁注: maps in erlang (and, hence, in elixir) are not ordered. That means, if you want the resulting list to be sorted by name
, or by score
, you should explicitly Enum.sort/2
it:
Enum.sort(..., & &1.score > &2.score)
#⇒ [%{name: "Elixir", score: 12},
# %{name: "Javascript", score: 1}]
一种简单的方法是使用 Enum.group_by/3
to group the items by name, then Enum.sum/1
对分数求和:
list
|> Enum.group_by(& &1.name, & &1.score)
|> Enum.map(fn {name, score} -> %{name: name, score: Enum.sum(score)} end)
输出:
[%{name: "Elixir", score: 12}, %{name: "Javascript", score: 1}]
如果您希望创建和使用更通用的解决方案,您可以创建自己的合并模块。
defmodule Merger do
def merge_by(enumerable, name_fun, merge_fun) do
enumerable
|> Enum.group_by(name_fun)
|> Enum.map(fn {_name, items} -> Enum.reduce(items, merge_fun) end)
end
end
list = [
%{score: 1, name: "Javascript"},
%{score: 2, name: "Elixir"},
%{score: 10, name: "Elixir"}
]
Merger.merge_by(list, & &1.name, &%{&1 | score: &1.score + &2.score})
# => [%{name: "Elixir", score: 12}, %{name: "Javascript", score: 1}]