Search code examples
listdictionaryzipelixir

Elixir: Convert a list into a map with integer keys


How do you go from a list: ~w[dog cat sheep]

to a map with integer keys: %{1=> "dog", 2=> "cat", 3=> "sheep"}

My Attempt:

iex(1)> list = ~w[dog cat sheep]
["dog", "cat", "sheep"]
iex(2)> list |> Enum.with_index|>Enum.map(fn({a,b})->{b+1,a} end)|> Enum.into %{}

%{1=> "dog", 2=> "cat", 3=> "sheep"}

Is there a simpler way?


Solution

  • Here's a one-liner version:

    for {v, k} <- ~w[dog cat sheep] |> Enum.with_index, into: %{}, do: {k+1, v}
    

    And here's the same thing as a reusable function in a module:

    defmodule Example do
      def to_indexed_map(list, offset \\ 0)
          when is_list(list)
          and is_integer(offset),
        do: for {v, k} <- list |> Enum.with_index,
          into: %{},
          do: {k+offset, v}
    end
    

    Example usage:

    iex> list = ~w[dog cat sheep]
    ["dog", "cat", "sheep"]
    iex> Example.to_indexed_map(list)
    %{0 => "dog", 1 => "cat", 2 => "sheep"}
    

    Minor Update: A less concise, but more performant version (roughly 2x faster) is shown below.

    defmodule Example do
      def to_indexed_map(list, offset \\ 0)
          when is_list(list)
          and is_integer(offset),
        do: to_indexed_map(list, offset, [])
    
      defp to_indexed_map([], _k, acc),
        do: :maps.from_list(acc)
      defp to_indexed_map([v | vs], k, acc),
        do: to_indexed_map(vs, k+1, [{k, v} | acc])
    end