2017-02-15 50 views
3

[1,2,3,4]這樣的列表轉換爲地圖%{1=>2, 3=>4}的優雅高效方法是什麼?我寫到:Elixir - 將列表轉換爲地圖

 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

一個重要的問題是:當原始列表中有奇數個元素時,你想要發生什麼?例如,刪除最後一個值或爲其分配一個默認值「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

你並不需要單獨調用Enum.map,你可以做,在Map.new

[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 

該解決方案比其他er解決方案提出。我寫了下面的模塊能夠標杆不同的解決方案:

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 

所以對於小名單的bechmark如下:

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] 

結果是以微秒爲單位的平均運行時間,並且越少越好。正如你所看到的,在這種情況下,好的ol'尾遞歸(Benchmark.alex/1)做得更好。

我希望這個幫助:)

5

使用Enum.into,需要一個轉換函數爲第二個參數:

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

您可以使用enum.chunk

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

拆分每個枚舉樂趣返回一個新值的元素。 返回列表的列表。