Elixir - 将列表转换为地图

时间:2017-02-15 23:32:41

标签: elixir

[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)

但这似乎并不优雅。这样做有更优雅,更清洁的方式吗?

4 个答案:

答案 0 :(得分:18)

您可以避免额外拨打Enum.map/2,并使用Map.new/2直接构建新地图:

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

更新:此答案的上一版本使用了chunk/2但已被弃用,而不是chunk_every/2

答案 1 :(得分:14)

您可以使用Enum.chunk_every/2

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

答案 2 :(得分:8)

使用Enum.into,它将转换函数作为第二个参数:

 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>  

答案 3 :(得分:6)

您可以使用尾递归来完成此任务:

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

  def f([x, y | xs], acc), do: f(xs, [{x, y} | acc])
  def f(_, acc), do: Map.new(acc)
end

此解决方案比其他提出的解决方案更具时间效率。我编写了以下模块,以便能够对不同的解决方案进行基准测试:

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_every(2, 2, :discard)
    |> Enum.map(fn [x, y] -> {x, y} end)
    |> Map.new()
  end

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

  # Patrick's solution
  def patrick(xs) do
    xs
    |> Enum.chunk_every(2, 2,:discard)
    |> Enum.into(%{}, 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

  # 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]
    |> Stream.map(fn f -> timed(f, list, times) end)
    |> Enum.sort(fn {_, x}, {_, y} -> x < y end)
  end
end

因此小名单的bechmark如下:

iex(1)> Benchmark.test(0..100)
[alex: 10.1, sheharyar: 27.7, nietaki: 27.8, patrick: 29.2, chip: 63.5]

对于大型列表如下:

iex(2)> Benchmark.test(0..1_000_000)
[
  alex: 197784.6,
  patrick: 369645.9,
  nietaki: 370870.2,
  sheharyar: 372616.4,
  chip: 794839.6
]

结果是平均运行时间(以微秒为单位),越少越好。如你所见,好的&#39;尾递归(Benchmark.alex/1)在这种情况下做得更好。

我希望这有帮助:)