Я инстинктивно идти на Enum.map
подход:
input
|> Enum.map(fn{k, v} -> {k, v + 1} end)
|> Map.new()
Это делает ваше намерение ясно, что это легко читать, он будет работать для всех Enum
с и должно быть в порядке точки зрения производительности.
Когда речь заходит о производительности, Enum.map/2
ваш второй выбор после того, как :maps.map/2
предложил Dogbert:
##### With input Large (100 0000 items) #####
Name ips average deviation median
:maps.map 104.52 9.57 ms ±9.91% 9.38 ms
Enum.map 54.07 18.49 ms ±8.32% 18.41 ms
Stream.map 44.33 22.56 ms ±14.86% 22.50 ms
Enum.reduce 25.39 39.38 ms ±22.03% 37.61 ms
for comprehension 25.01 39.99 ms ±20.95% 37.30 ms
Comparison:
:maps.map 104.52
Enum.map 54.07 - 1.93x slower
Stream.map 44.33 - 2.36x slower
Enum.reduce 25.39 - 4.12x slower
for comprehension 25.01 - 4.18x slower
Относительной эффективность различных подходов зависит от размера входных карт, но Enum.map
всегда второй самый быстрый вариант (по крайней мере, на моей машине).
Вот код теста, используя Benchee:
defmodule Mix.Tasks.Benchmark.MapMap do
use Mix.Task
def run(_args) do
inputs = %{
"Tiny (10 items)" => produce_map(10),
"Small (100 items)" => produce_map(100),
"Medium (10 000 items)" => produce_map(10_000),
"Large (100 0000 items)" => produce_map(100_000),
}
Benchee.run(%{
"Enum.reduce" =>
fn(input) ->
Enum.reduce(input, %{}, fn({k,v}, acc) -> Map.put(acc, k, mapper(v)) end)
end,
"for comprehension" =>
fn(input) ->
for {k,v} <- input, do: {k, mapper(v)}, into: %{}
end,
":maps.map" =>
fn(input) ->
:maps.map(fn(_k, v) -> mapper(v) end, input)
end,
"Enum.map" =>
fn(input) ->
input
|> Enum.map(fn{k, v} -> {k, mapper(v)} end)
|> Map.new()
end,
"Stream.map" =>
fn(input) ->
input
|> Stream.map(fn{k, v} -> {k, mapper(v)} end)
|> Map.new()
end
}, [time: 1, warmup: 1, inputs: inputs])
end
def mapper(x), do: x + 1
defp produce_atom(idx) do
idx = Integer.to_string(idx)
String.to_atom("a" <> idx)
end
defp produce_map(size) do
1..size
|> Enum.map(fn(i) -> {produce_atom(i), i} end)
|> Map.new
end
end
Side Примечание: Там в Map.new/2
, которая делает как создание карты и преобразование значений Enum в ключи карты и значения, так что вы можете иметь аналогичный подход без Enum.map
.
Я думаю, что второй вариант не так уж плох. – JustMichael
Существует также ': maps.map/2', который может быть более эффективным, чем любой из этих двух. – Dogbert