Transform a list into an map with indexes using Enum module

Hi,

I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. I’ve managed to do this using the following code:

  def indexed_map(list, index \\ 1, step \\ 1) do
    to_indexed_map(list, index,step, %{})
  end
  defp to_indexed_map([h|t], i, s, acc),do: to_indexed_map(t, i + s, s, Map.put(acc, i, h))
  defp to_indexed_map([], _,  _, acc), do: acc

Basically if I apply this function to

[100, 200, 300]

it will return

%{1 => 100, 2 => 200, 3 => 300}

Is there any way to the same using Elixir’s standard library? I tried Enum.map, Enum.reduce and even Enum.map_reduce but I just couldn’t figure out how to it using them.

Thanks

3 Likes

Something like this should work:

list = [100, 200, 300]
Stream.with_index(list, 1) |> Enum.reduce(%{}, fn({v,k}, acc)-> Map.put(acc, k, v) end)

Or this :stuck_out_tongue:

1..length(list) |> Stream.zip(list) |> Enum.into(%{})
8 Likes

There is prob a better way then this:

  list |> Enum.with_index(1) |>Enum.map(fn {k,v}->{v,k} end) |> Map.new
7 Likes

Thanks.

I always used only this form:

fn(value, acc) -> ... end

How exactly the {v, k} is filled by Elixir like in:

fn({v,k}, acc)-> ... end)

?

EDIT: Nevermind, I figured it out in the docs (is the Enum.with_index function)

Thank you :slight_smile:

3 Likes

The {v, k} is just a pattern match on the values coming in. .with_index returns tuples of the value and the index.

Like this:

Enum.with_index([1,2,3]) => [{100, 1}, {200, 2}, {300, 3}]

So for each value I just destruct the passed in tuple, like {v, k} = {100, 1}.

2 Likes

Got it :wink: Thanks!

Or shorter (by actually creating the tuple in-order instead of needing to swap it and not getting the length of a list, which can be O(n) instead of O(1)):

iex> list = [:a,:b,:c,:d,:e]
iex> Stream.zip(Stream.iterate(0, &(&1+1)), list) |> Enum.into(%{})
%{0 => :a, 1 => :b, 2 => :c, 3 => :d, 4 => :e}

An aside, I wish 0.. was a shortcut for (Stream.iterate(0, &(&1+1)), or maybe 0...1 means (Stream.iterate(0, &(&1+1)) like 0...-4 means (Stream.iterate(0, &(&1-4)) or so, ah well I wish, but it does not for now. Hmm, lot of ideas popping into mind for such a syntax, we need a Stream.seq/1.

6 Likes

You might like the Sequences Hex package that I made a while back. It defines streams for quite a few common numeric sequences. :slight_smile:

6 Likes

Huh, nice! Not ran across that yet. :slight_smile: