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 (and, hence, in ) 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}]