2017-05-23 6 views
0

エリクシールでは、Mapをその値でフィルタリングする効率的な方法は何でしょうか。エリクシールの値でマップをフィルタリングする効率的な方法

今、私は以下のソリューション

%{foo: "bar", biz: nil, baz: 4} 
|> Enum.reject(fn {_, v} -> is_nil(v) end) 
|> Map.new 

を持っているこのソリューションは、私にはかなり非効率です。 Mapで呼び出された場合、Enum.reject/2Keywordsを返します。 Mapが必要なので、Keywordsを私に戻すにはMap.new/1に電話する必要があります。

Enum.reject/2は一度、その後、おそらくMapを反復するために持っているので、これは非効率的と思われる、Map.new/1Keywords別の時間を反復しなければなりません。

もっと効率的なソリューションは何でしょうか?

答えて

2

あなたはマップをフィルタリングし、任意の中間リストを作成しない、:maps.filter/2を使用することができます。

iex(1)> :maps.filter fn _, v -> v != nil end, %{foo: "bar", biz: nil, baz: 4} 
%{baz: 4, foo: "bar"} 

は簡単なベンチマークでは、これがEnum.filter + Map.newよりも高速です確認:

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) 
{44728, 
%{48585 => 1, 60829 => 2, 12995 => 1, 462 => 2, 704 => 2, 28954 => 2, 
    29635 => 2, 78798 => 1, 92572 => 1, 89750 => 2, 39389 => 2, 62855 => 2, 
    79313 => 1, 92062 => 2, 61871 => 1, 92856 => 2, 75920 => 1, 59922 => 1, 
    37912 => 2, 30420 => 2, 51211 => 2, 7994 => 2, 78269 => 2, 9765 => 2, 
    38352 => 2, 6653 => 1, 82555 => 2, 54031 => 2, 45138 => 1, 41351 => 1, 
    40746 => 1, 5961 => 1, 66704 => 2, 33823 => 1, 47603 => 1, 86873 => 1, 
    81009 => 2, 96255 => 1, 36219 => 1, 1328 => 2, 33314 => 1, 54477 => 2, 
    40189 => 2, 27028 => 1, 31676 => 1, 94037 => 1, 32388 => 1, 4351 => 1, 
    46309 => 1, ...}} 
{28638, 
%{48585 => 1, 60829 => 2, 12995 => 1, 462 => 2, 704 => 2, 28954 => 2, 
    29635 => 2, 78798 => 1, 92572 => 1, 89750 => 2, 39389 => 2, 62855 => 2, 
    79313 => 1, 92062 => 2, 61871 => 1, 92856 => 2, 75920 => 1, 59922 => 1, 
    37912 => 2, 30420 => 2, 51211 => 2, 7994 => 2, 78269 => 2, 9765 => 2, 
    38352 => 2, 6653 => 1, 82555 => 2, 54031 => 2, 45138 => 1, 41351 => 1, 
    40746 => 1, 5961 => 1, 66704 => 2, 33823 => 1, 47603 => 1, 86873 => 1, 
    81009 => 2, 96255 => 1, 36219 => 1, 1328 => 2, 33314 => 1, 54477 => 2, 
    40189 => 2, 27028 => 1, 31676 => 1, 94037 => 1, 32388 => 1, 4351 => 1, 
    46309 => 1, ...}} 
0

これは少し高価かもしれませんが、より多くの宣言的なことがあります。 また、あなたのコレクションにどれくらいの大きさがあるのか​​、このフィルタを最適化することが合理的であると考えてください。

しかし、私はここに、あなたの懸念を理解し、私がやったことです:

%{foo: "bar", biz: nil, baz: 4} 
|> Enum.reduce(%{}, filter_nil_values/2) 
filter_nil_values/2

defp filter_nil_values({_k, nil}, accum), do: accum 
defp filter_nil_values({k, v}, accum), do: Map.put(accum, k, v) 

として定義されて

私は1ライン機能でこれを実行しようとしましたが、それはひどいね。

0

この場合、理解は良いアイデアとなるでしょう。

map = %{baz: 4, biz: nil, foo: "bar"} 
for {key, value} <- map, !is_nil(value), into: %{}, do: {key, value} 
+0

これは中間リストを作成すべきではありませんが、実際には、for、into:%{} 'が作成よりもさらに遅くなることがわかりました。 'Enum.filter/2'のリストを' Map.new/1'に渡します。私は私の答えに掲載されたベンチマークで試してみましたが、これはOPのコードよりも私の約20〜25%遅く実行されます。 – Dogbert

+0

ありがとう:)私はerlangの解決策が最速のものであることに驚くことはありません。 – PatNowak

関連する問題