2017-02-15 75 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 

拆分每个枚举乐趣返回一个新值的元素。 返回列表的列表。