在地图上执行地图操作的最常用方法是什么?
我想到了两种方法:
Enum.reduce(%{a: 1, b: 2}, %{}, fn({k,v}, acc) -> Map.put(acc, k, v+1) end)
# => %{a: 2, b: 3}
for {k,v} <- %{a: 1, b: 2}, do: {k, v+1}, into: %{}
# => %{a: 2, b: 3}
有没有更好的选择,我没有想到?如果没有,上述哪一个更受欢迎?
答案 0 :(得分:1)
我本能地采用Enum.map
方法:
input
|> Enum.map(fn{k, v} -> {k, v + 1} end)
|> Map.new()
它使你的意图清晰,易于阅读,它适用于所有Enum
,并且在性能方面应该是正常的。
在性能方面,Enum.map/2
是您在Dogbert提出的:maps.map/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
。