Dictionary Elixir中按值过滤贴图的有效方法
在Elixir中,通过值过滤Dictionary Elixir中按值过滤贴图的有效方法,dictionary,filter,elixir,Dictionary,Filter,Elixir,在Elixir中,通过值过滤映射的有效方法是什么 现在我有以下的解决方案 %{foo: "bar", biz: nil, baz: 4} |> Enum.reject(fn {_, v} -> is_nil(v) end) |> Map.new 这个解决方案在我看来效率很低。在映射上调用时,枚举拒绝/2返回关键字。因为我想要一张地图,所以我需要调用Map.new/1将关键字转换回我 这似乎效率低下,因为Enum.reject/2必须在Map上迭代一次,然后大概是Map。new
映射的有效方法是什么
现在我有以下的解决方案
%{foo: "bar", biz: nil, baz: 4}
|> Enum.reject(fn {_, v} -> is_nil(v) end)
|> Map.new
这个解决方案在我看来效率很低。在映射上调用时,枚举拒绝/2
返回关键字
。因为我想要一张地图
,所以我需要调用Map.new/1
将关键字
转换回我
这似乎效率低下,因为Enum.reject/2
必须在Map
上迭代一次,然后大概是Map。new/1
必须在关键字上迭代另一次
什么是更有效的解决方案?可能会有点贵,但它更具声明性,IMO会增加更多的价值。
还要考虑你的收藏有多大,如果优化这个过滤器是有意义的。
然而,我理解你的担忧,因此我所做的如下:
%{foo: "bar", biz: nil, baz: 4}
|> Enum.reduce(%{}, filter_nil_values/2)
其中filter\u nil\u values/2
定义为
defp filter_nil_values({_k, nil}, accum), do: accum
defp filter_nil_values({k, v}, accum), do: Map.put(accum, k, v)
我尝试在一行函数中执行此操作,但它看起来很糟糕。您可以使用:maps.filter/2
,它过滤地图,并且不创建任何中间列表:
iex(1)> :maps.filter fn _, v -> v != nil end, %{foo: "bar", biz: nil, baz: 4}
%{baz: 4, foo: "bar"}
一个简单的基准测试证实了这比枚举过滤器更快
map = for i <- 1..100000, into: %{}, do: {i, Enum.random([nil, 1, 2])}
IO.inspect :timer.tc(fn ->
map
|> Enum.reject(fn {_, v} -> is_nil(v) end)
|> Map.new
end)
IO.inspect :timer.tc(fn ->
:maps.filter fn _, v -> v != nil end, map
end)
在本例中,理解是一个好主意,因为它也不会创建中间列表并返回一个映射:
map = %{baz: 4, biz: nil, foo: "bar"}
for {key, value} <- map, !is_nil(value), into: %{}, do: {key, value}
map=%{baz:4,biz:nil,foo:bar}
对于{key,value}也可以这样写:
m = %{foo: "bar", biz: nil, baz: 4}
Enum.reduce(m, m, fn
{key, nil}, acc -> Map.delete(acc, key)
{_, _}, acc -> acc
end)
如果m
中几乎没有nil
值,那么上面的代码是非常有效的。这不应该创建一个中间列表,但在实践中,我发现for…,into:%{}
比使用Enum.filter/2
创建一个列表并将其传递给Map.new/1
还要慢。我用我在答案中发布的基准测试进行了测试,结果比OP的代码慢了20-25%。谢谢:)我并不惊讶erlang的解决方案是最快的解决方案。如果有人想知道,:maps.filter
m = %{foo: "bar", biz: nil, baz: 4}
Enum.reduce(m, m, fn
{key, nil}, acc -> Map.delete(acc, key)
{_, _}, acc -> acc
end)