Эликсир Аккумулятор Список Карт - PullRequest
0 голосов
/ 24 февраля 2020

Можете ли вы помочь мне реализовать один аккумулятор из списка карт?

[
 %{
    score: 1,
    name: "Javascript",
 },
 %{
    score: 2,
    name: "Elixir",
 },
 %{
    score: 10,
    name: "Elixir",
 }
]

Результат должен быть:

[
    %{
        score: 12,
        name: "Elixir",
    },
    %{
        score: 1,
        name: "Javascript",
    }
]

Буду признателен за ваше предложение.

С уважением

Ответы [ 3 ]

1 голос
/ 24 февраля 2020

Простой способ - использовать Enum.group_by/3 для группировки элементов по имени, затем 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}]
1 голос
/ 25 февраля 2020

Если вы хотите создать и использовать более обобщенное решение, вы можете создать свой собственный модуль Merger.

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}]
1 голос
/ 24 февраля 2020

Если исходный список хранится в локальной переменной input, можно начать с Enum.reduce/3, используя 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 гораздо менее читабелен), go далее и 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}]

Sidenote: карты в (и, следовательно, в ) не заказан . Это означает, что если вы хотите, чтобы результирующий список был отсортирован по name или score, вам следует явно Enum.sort/2 it:

Enum.sort(..., & &1.score > &2.score)
#⇒ [%{name: "Elixir", score: 12},
#   %{name: "Javascript", score: 1}]
...