2017-02-15 4 views
3

[1,2,3,4]のようなリストを%{1=>2, 3=>4}に変換するためのエレガントで効率的な方法は何でしょうか?私はこれを書いた:エリクサー - マップにリストを変換する

 Enum.reduce([1,2,3,4],%{}, fn(item, acc) -> 
     case Map.get(acc, :last) do 
      nil -> 
      Map.put(acc, :last, item) 
      last -> 
      acc = Map.put(acc, item, last) 
      Map.drop(acc, [:last]) 
     end 
     end) 

しかし、これは非常にエレガントなようではありません。これを行うためのよりエレガントで清潔な方法がありますか?

+1

重要な質問の1つは、元のリストに奇数の要素がある場合に、何をしたいのですか?最後の値を削除するか、例えば 'nil'のデフォルト値を代入しますか? 'Enum.chunk'を使用するすべてのソリューションでは、' Enum.chunk(2、2、[nil]) 'を介してデフォルト値を与えることができますが、普通の' Enum.chunk(2) 'は完全には満たされない。 –

答えて

10

あなたはEnum.chunk/2を使用することができます。

[1, 2, 3, 4] 
    |> Enum.chunk(2) 
    |> Enum.map(fn [a, b] -> {a, b} end) 
    |> Map.new 
10

あなたはMap.newでそれを行うことができ、別途Enum.mapを呼び出す必要はありません。

[1,2,3,4] 
|> Enum.chunk(2) 
|> Map.new(fn [k, v] -> {k, v} end) 
3

あなたがこれを達成するために末尾再帰を使用することができます。

defmodule Test do 
    def f(list, acc \\ []) 

    def f([x, y | xs], acc), do: f(xs, [{x, y} | acc]) 
    def f(_, acc), do: Enum.into(acc, %{}) 
end 

このソリューションは、解決策が提案されました。だから、小さなリストのbechmarkは以下の通りです

defmodule Benchmark do 

    # My solution 
    def alex(xs, acc \\ []) 
    def alex([x, y | xs], acc), do: alex(xs, [{x, y} | acc]) 
    def alex(_, acc), do: Map.new(acc) 

    # nietaki's solution 
    def nietaki(xs) do 
    xs 
    |> Enum.chunk(2) 
    |> Enum.map(fn [x, y] -> {x, y} end) 
    |> Map.new() 
    end 

    # Sheharyar's solution 
    def sheharyar(xs) do 
    xs 
    |> Enum.chunk(2) 
    |> Map.new(fn [x, y] -> {x, y} end) 
    end 

    # Your solution 
    def chip(xs) do 
    Enum.reduce(xs, %{}, fn(item, acc) -> 
     case Map.get(acc, :last) do 
     nil -> 
      Map.put(acc, :last, item) 
     last -> 
      acc = Map.put(acc, item, last) 
      Map.drop(acc, [:last]) 
     end 
    end) 
    end 

    # Patrick's solution 
    def patrick(xs) do 
    xs 
    |> Enum.chunk(2) 
    |> Enum.into(%{}, fn [x, y] -> {x, y} end) 
    end 

    # Function to do the time benchmarks. 
    def timed(f, list, times \\ 10) do 
    tests = 
     for _ <- 0..times do 
     :timer.tc(fn -> apply(__MODULE__, f, [list]) end) |> elem(0) 
     end 
    avg = Enum.sum(tests)/times 
    {f, avg} 
    end 

    # Test function. 
    def test(list, times \\ 10) do 
    list = Enum.to_list(list) 
    [:alex, :chip, :patrick, :nietaki, :sheharyar] 
    |> Enum.map(fn f -> timed(f, list, times) end) 
    |> Enum.sort(fn {_, x}, {_, y} -> x < y end) 
    end 
end 

iex(1)> Benchmark.test(0..4) 
[alex: 0.2, nietaki: 0.5, sheharyar: 0.6, chip: 0.8, patrick: 4.4] 

そして、次の大規模なリストのために:

iex(2)> Benchmark.test(0..1_000_000) 
[alex: 143105.7, nietaki: 241233.4, sheharyar: 254751.9, patrick: 501678.9, chip: 801616.5] 
私は別のソリューションベンチマークのことができるようにするには、以下のモジュールを書きました

結果は平均実行時間(マイクロ秒)で、それ以下の方が良いです。あなたが見ることができるように、この場合、良いオールテイル再帰(Benchmark.alex/1)が良い仕事をします。

私はこのヘルプ:) Enum.intoを使用して

5

、2番目のパラメータとして変換関数を取る願っています:

list 
|> Enum.chunk(2) 
|> Enum.into(%{}, fn [a, b] -> {a, b} end) 
0

あなたは、すべての列挙

[1, 2, 3, 4] 
|> Enum.chunk(2) 
|> Enum.map(fn [a, b] -> {a, b} end) 
|> Map.new 

分割をenum.chunk使用することができますfunが新しい値を返す要素。 リストのリストを返します。

関連する問題