I'd like to take the element of the same index from multiple lists

Is there a way to take multiple lists and get all the elements at their n th index and pass them to the function?
For example, list1 (= [1,3,5]) and list2 (= [2,4,6]) are added,then list (=[3,7,11]) is created.
What I wrote below is wrong, but I imagine such usage.

defmodule TwoLists do
  def sum(list1,list2) do
    Enum.map(list1,list2,fn(a,b)->a+b end) 
  end
end

iex> list1 = [1,3,5]
iex> list2 = [2,4,6]
iex> TwoLists.sum(list1,list2)
#-->Enum.map/3 is undefined error

I’d like you to show me the way to implement.
Thanks

One way I see is the following one:

defmodule ListSum do
  def sum(list1, list2, total \\ [])

  def sum([], [], total) do
    Enum.reverse(total)
  end

  def sum([h1 | t1], [], total) do
    sum(t1, [], [h1 | total])
  end

  def sum([], [h2 | t2], total) do
    sum([], t2, [h2 | total])
  end

  def sum([h1 | t1], [h2 | t2], total) do
    sum(t1, t2, [h1 + h2 | total])
  end
end

This implementation has the advantage to work with list of different lengths.
You can also code an implement using for and Enum.at but lists must have same length is this case

6 Likes

You might use Enum.zip.

iex(1)> list1 = [1,3,5]
[1, 3, 5]
iex(2)> list2 = [2,4,6]
[2, 4, 6]
iex(3)> Enum.zip list1, list2
[{1, 2}, {3, 4}, {5, 6}]
iex(4)> list3 = [2,4,6,8]    
[2, 4, 6, 8]
iex(5)> Enum.zip list1, list3
[{1, 2}, {3, 4}, {5, 6}]

but it will return a number of elements corresponding to the smallest list in case they are not the same length.

You can use more than two lists

iex(6)> Enum.zip [list1, list2, list3]
[{1, 2, 2}, {3, 4, 4}, {5, 6, 6}]

And for your use case…

iex(7)> list1 |> Enum.zip(list2) |> Enum.map(& (elem(&1, 0) + elem(&1, 1)))
[3, 7, 11]
3 Likes

Thanks so much for reply.
I think this is very smart way to solve my problem.

Yes, I solved it by using Enum.at as below, but length of these lists must be same like you said.

def MultiLists(list1,list2) do
    Enum.map(0..length(list1)-1,fn(n)-> 
        Enum.at(list1,n) + Enum.at(list2,n) 
    end)    
end

Thanks. I learned a lot.

Thank you so much for lucid explanation.
I have never used Enum.zip, so I didn’t think of your nice idea.
I have to be careful about length of lists in this method.

Thanks, I try to use Enum.zip more.

This is terrible solution, because Enum.at/2 has linear complexity. So in your case the complexity of this function is quadratic instead of linear.

3 Likes

Definitely go with @domvas’ solution. It runs faster and doesn’t break when lists are of different length.

1 Like

This is such an elegant solution! I know it works but I’m trying to understand exactly how, and have a very basic question to ask: I’ve never seen a function def not followed by do…end like the first def in this module; what does it mean? Thank you!

the function without the do block is a header function, it is needed when we declare default arguments on matching functions, see that only that function has the total with a default value. Without it you would get a definitions with multiple clauses and default values require a header. error message from the compiler.
Roughly the compiler will create a sum/2 function calling the matching functions sum/3 with a default value on the third argument.

You can see more examples at Elixir School.

:smile:

2 Likes