私は本能的にEnum.map
アプローチのために行くだろう:
input
|> Enum.map(fn{k, v} -> {k, v + 1} end)
|> Map.new()
それは、あなたの意図が明確になり、それがすべてのEnum
のために働くだろうとOK性能面である必要があり、読みやすいです。それがパフォーマンスになると
、Enum.map/2
はDogbertによって提案:maps.map/2
後に2番目の選択肢です:
##### 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
サイドノート:あなたが持つことができるので、マップの作成とマップのキーと値に列挙値の変換の両方を行うれ、Map.new/2
ありますEnum.map
のない同様のアプローチ。
私は第2の選択肢がそれほど悪くないと思います。 – JustMichael
':maps.map/2'もあり、これらの2つのどちらよりも効率的です。 – Dogbert