Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concatenating multiple lists in Elixir

Tags:

elixir

I am learning Elixir and found myself having to concat multiple lists together where I have a beginning, middle, and end. Simplified example:

a = [1,2]
b = [3,4]
c = [5,6]
a ++ b ++ c
> [1, 2, 3, 4, 5, 6]

I am doing this thousands of times in a stream and want to be Elixirific about it.

Part 1)

I wrote a function to handle this, there is probably something that does this for me, but I don't see it. Should I be using built-in Elixir function for this?

def append(front, back) when is_list(front) when is_list(back) do
  front
  |> Enum.reverse
  |> Enum.reduce(back, &([&1 | &2]))
end

Or should I have just done this and it will become more natural for me over time?

[1, 2]
|> Enum.reverse 
|> Enum.reduce([3, 4], &([&1 | &2]))
|> Enum.reverse
|> Enum.reduce([5, 6], &([&1 | &2]))

Part 2)

Does the order in which I call the parts together matter?

Way 1:
[1, 2]
|> append([3, 4])
|> append([5, 6])

...
Way 2:
end = append([3, 4], [5, 6])
append([1, 2], end)

Will the intermediary list be re-used in both scenarios since both are appending header pointers?

Any help on this would be great, cheers.

like image 856
blu Avatar asked Jan 24 '17 06:01

blu


People also ask

How do you add two lists in Elixir?

How do you add two lists in Elixir? Idiom #166 Concatenate two lists. Create the list ab containing all the elements of the list a, followed by all the elements of the list b. auto ab = a; ab.

How to Append to a list in Elixir?

In Elixir and Erlang we use `list ++ [elem]` to append elements.


1 Answers

No matter what approach you use, you will end up at cloning at least all lists except the last one, since you cannot append to a linked list without cloning it. So if you know you want to concatenate the lists (i.e. you cannot modify your code to accept a nested list like [a, b, c]), I would suggest a ++ b ++ c since ++ is implemented in Erlang's C code, which should be pretty much as fast as possible, and definitely faster than manually concatenating with Enum.reverse/1 and Enum.reduce/3.

Here's a tiny microbenchmark comparing a ++ b ++ c vs a |> append(b) |> append(c):

defmodule BasicBench do
  use Benchfella

  bench "++", [a: gen(), b: gen(), c: gen()] do
    a ++ b ++ c
  end

  bench "append", [a: gen(), b: gen(), c: gen()] do
    a |> append(b) |> append(c)
  end

  def append(front, back) when is_list(front) when is_list(back) do
    front
    |> Enum.reverse
    |> Enum.reduce(back, &([&1 | &2]))
  end

  def gen, do: Enum.to_list(1..1000)
end

and the output:

benchma iterations   average time
++          100000   10.22 µs/op
append       20000   75.79 µs/op
like image 80
Dogbert Avatar answered Sep 19 '22 14:09

Dogbert