odohMei7

odohMei7

Append to list after Enum.map. How to do this efficiently?

How can I efficiently append one entry to a list? Is this the best solution:

history = [{1,100}, {2,300}, {3,200}] 
current = {4,150}
chart = Enum.map(history,&Tuple.to_list(&1)) ++ [Tuple.to_list(current)]|>Jason.encode!

It does not feel good to scan the list twice, once during Enum.map and once for appending to the list which is O(length(list)) as I have read. Is there a better method to only loop through the list once (think of a long history list)?

I did not benchmark and what I have done above seems to be sufficiently performant in my use case so this is rather a theoretical question that I am interested in.

Bonus question: Is there any method to avoid the Tuple.to_list before I pipe into Jason.encode, this also feels unnecessary but unfortunately Jason.encode cannot handle tuples.

Marked As Solved

sabiwara

sabiwara

Elixir Core Team

From a purely theoretical perspective, you could implement a variant of Enum.map/2 that appends an element at the end, such as:

  def map_append([], _fun, last), do: [last]
  def map_append([head | tail], fun, last) do
    [fun.(head) | map_append(tail, fun, last)]
  end

But as you pointed out, this is probably premature optimization and I wouldn’t recommend doing this in production code :wink:

Also Liked

l00ker

l00ker

I would like to mention that Enum.reverse in turn calls :lists.reverse if the first argument (the enumerable) is a List.

With that said, after reading the following in Programming Erlang (2nd edition) by Joe Armstrong, I felt much better about using Enum.reverse with a List as the enumerable:


If the order matters, then call lists:reverse/1, which is highly optimized.

Note: Whenever you want to reverse a list, you should call lists:reverse and nothing else. If you look in the source code for the module lists, you’ll find a definition of reverse. However, this definition is simply used for illustration. The compiler, when it finds a call to lists:reverse, calls a more efficient internal version of the function.

So, with a long list, most of the time I’ll always add elements to a list head and then call Enum.reverse at the end and trust that Joe and Erlang have my back. :slight_smile:

But as Joe points out a little later:

The best thing to do is first write your programs as clearly as possible and then, if there are performance problems, measure before making any optimizations.

lud

lud

Any solution I could imagine would be a wrapper around Tuple.to_list anyway.

For the double scan, unless your history is very, very long, I would not try to optimize that, what you do is fine. If your really want a performance optimization here then I would just build the history in reverse order, and prepend the new entry instead of appending it.

sabiwara

sabiwara

Elixir Core Team

Just like Enum.map/2 (actually :lists.map/2), this implementation is body recursive, not tail recursive.

A tail-recursive version would need to call Enum.reverse/2 at the end, just like the example provided by @eksperimental.

Either one is probably fine in this case and the performance should be roughly equivalent when building lists, cf the Erlang efficiency guide and the article it refers to. Body recursion might even be faster / use less memory depending on the function being optimized, list size, your architecture etc…

odohMei7

odohMei7

Thank you, yes, I should just keep it as is and go on. I suffer from premature optimization because I come from C++ :smile:, was just wondering if there is a thing here that I should know about.

eksperimental

eksperimental

Yes, there is a way which is optimal.
You only iterate over your list twice.

iex> Enum.reduce(history, [], fn x, acc  ->
...>   [Tuple.to_list(x) | acc]
...> end)
...> |> :lists.reverse([Tuple.to_list(current)])
[[1, 100], [2, 300], [3, 200], [4, 150]]

Where Next?

Popular in Questions Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement