1
votes

Given two lists and a third list which has values that map to the permutations of the first two, I would like to create a Map of list1's key, list2's key, and list3's value. How can I get list3's value at a certain index if I am inside the loop?

Using Enum.at is not the correct solution for this, I understand - it will traverse the whole list on every iteration. And if I try [head | tail] = list3, it looks like I can't just set list3 = tail for the next loop.

list1 = [1,2]
list2 = [3,4]
list3 = 'wxyz'

Enum.each(list1), fn i ->
  Enum.each(list2), fn j ->
    # Would like to just chop off the first value of list3 and 
    # pass the tail into the next iteration
  end
end
1
Since my solution is unworkable regardless of Enum.at, I've removed it. But the idiomatic way to code a nested loop in Elixir is with Comprehensions.Onorio Catenacci
BTW you will find it really hard to do anything better than O(n) performance in this case because an Elixir list is a linked list under the hood.Onorio Catenacci

1 Answers

3
votes

As a longtime rubyist, throwing down .each became about as natural as breathing for me. After writing Elixir for a while now, .each has almost become a code smell for me- whenever I use it by reflex, I end up going back to remove it, because it leads to awkward Elixir code.

As mentioned in comments, a comprehension is idiomatic for "looping": if you don't mind traversing 3 times, the following works:

result = for i <- list1, j <- list2, do: {i, j}
         |> Enum.zip(list3) 
         |> Enum.into(%{})

iex> result
%{{1, 3} => 119, {1, 4} => 120, {2, 3} => 121, {2, 4} => 122}

You may choose to write it as a full-fledged function instead, especially if your example was a simplification of something more complicated. The following functions only traverse list3 once:

defmodule MapLists do
  def map_it(l1, l2, l3, acc \\ %{})
  def map_it([], _l2, _l3, acc), do: acc
  def map_it([h|t], list2, list3, acc) do
    {res1, new_list_3} = do_map_it(h, list2, list3, %{})
    new_acc = Map.merge(acc, res1)
    map_it(t, list2, new_list_3, new_acc)
  end


  defp do_map_it(item, [], l3, acc), do: {acc, l3}
  defp do_map_it(item, [h2|t2], [h3|t3], acc) do
    new_acc = Map.put(acc, {item, h2}, h3)
    do_map_it(item, t2, t3, new_acc)
  end
end

and usage:

iex> MapLists.map_it([1,2],[3,4],'wxyz')
%{{1, 3} => 119, {1, 4} => 120, {2, 3} => 121, {2, 4} => 122}