Can anyone give me a recommendation on how to do this better? Is there any build in functions to Enum/List/Map/Etc I may not know about to do this efficiently and in a more readable way?
I have a data structure similar to this
[
%{customer_id: "123456", amount: %Money{amount: 332211, currency: :USD}},
%{customer_id: "234567", amount: %Money{amount: 321123, currency: :USD}},
%{customer_id: "345678", amount: %Money{amount: 123321, currency: :USD}},
%{customer_id: "123456", amount: %Money{amount: 112233, currency: :USD}}
]
and I want to aggregate the amounts based on customer_id so I end up with the following
[
%{customer_id: "123456", amount: %Money{amount: 444444, currency: :USD}}, # No longer duplicated
%{customer_id: "234567", amount: %Money{amount: 321123, currency: :USD}},
%{customer_id: "345678", amount: %Money{amount: 123321, currency: :USD}}
]
The way I am doing it now is
defmodule Aggregator do
def aggregate_entries(entries) do
res = entries
|> Enum.reduce(%{}, fn entry, acc ->
current_id = Map.get(entry, :customer_id)
value_in_accumulator = Map.get(acc, String.to_atom(current_id))
accumulate_updated_amount(acc, entry, current_id, value_in_accumulator)
end)
|> Enum.reduce([], fn {id, amount}, acc ->
[%{customer_id: id, amount: Money.new(amount, :USD)} | acc]
end)
end
defp accumulate_updated_amount(acc, entry, current_id, value_in_accumulator) do
if !is_nil(value_in_accumulator) do
aggregate_money_for_entry(entry, value_in_accumulator, acc)
else
acc |> Map.put(String.to_atom(current_id), Map.get(entry, :amount).amount)
end
end
defp aggregate_money_for_entry(entry, value_in_accumulator, acc) do
acc |> Map.replace!(String.to_atom(entry.customer_id), entry.amount.amount + value_in_accumulator)
end
end