Elixir — Sort Lists by Date

How to Sort Lists by Date using Elixir - Coletiv Blog

Currently, if you need to use Enum.sort on your Elixir project and want to sort lists by date, you may experience problems depending on the Elixir version you are using.

Recently, while working on a project at Coletiv, I needed to sort a list containing structs by their creation date.

iex(2)> items
    [
      %{id: 2, inserted_at: ~D[2019-01-01]},
      %{id: 1, inserted_at: ~D[2018-01-31]},
      %{id: 3, inserted_at: ~D[2020-03-01]}
    ]

First Try

My first approach was to use the Enum.sort function and compare the struct’s inserted date property:

[]|> Enum.sort(&(&1.inserted_at > &2.inserted_at))

After some tests, I found that the sorting was not working. Why? It was supposed to be an easy fix 🤔

Let’s go to IEx to do some tests.

Erlang/OTP 21 [erts-10.0.8] [source] [64-bit]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> dates = [~D[2018-01-31],~D[2019-02-01],~D[2020-03-01]]
[~D[2018-01-31], ~D[2019-02-01], ~D[2020-03-01]]
iex(2)> dates |> Enum.sort()
[~D[2019-02-01], ~D[2020-03-01], ~D[2018-01-31]]
iex(3)> dates |> Enum.sort(& &1 > &2)
[~D[2018-01-31], ~D[2020-03-01], ~D[2019-02-01]]
iex(4)> dates |> Enum.sort(& &1 < &2)
[~D[2019-02-01], ~D[2020-03-01], ~D[2018-01-31]]

Strange…

Second Try

After some investigation, I found this article and the possible problem here.

The collection types are compared using the following rules:

- Tuples are compared by size, then element by element.

- Maps are compared by size, then by keys in ascending term order, then by values in key order. In the specific case of maps’ key ordering, integers are always considered to be less than floats.

- Lists are compared element by element.

- Bitstrings are compared byte by byte, incomplete bytes are compared bit by bit.

Considering that a date is a struct, it’s normal that the order was wrong. So the possible solution is:

iex(5)> dates |> Enum.sort_by(fn d -> {d.year, d.month, d.day} end)
[~D[2018-01-31], ~D[2019-02-01], ~D[2020-03-01]]

For previous versions of elixir (like elixir v1.7.3), this is the solution. A little rudimentary but it works.

Third Try

In elixir v1.10, Jose Valim and his team introduce improvements to sort-based APIs in Enum, as you can see here.

Let’s go to IEx again to do some tests…

Erlang/OTP 21 [erts-10.0.8] [source] [64-bit]

Interactive Elixir (1.10.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> dates = [~D[2018-01-31],~D[2019-02-01],~D[2020-03-01]]
[~D[2018-01-31], ~D[2019-02-01], ~D[2020-03-01]]
iex(2)> dates |> Enum.sort()
[~D[2019-02-01], ~D[2020-03-01], ~D[2018-01-31]]
iex(3)> dates |> Enum.sort(&(Date.compare(&1, &2) != :lt))
[~D[2020-03-01], ~D[2019-02-01], ~D[2018-01-31]]
iex(4)> dates |> Enum.sort(Date)
[~D[2018-01-31], ~D[2019-02-01], ~D[2020-03-01]]
iex(5)> dates |> Enum.sort({:desc, Date})
[~D[2020-03-01], ~D[2019-02-01], ~D[2018-01-31]]
iex(6)> dates |> Enum.sort({:asc, Date})
[~D[2018-01-31], ~D[2019-02-01], ~D[2020-03-01]]

Voilá 🎉 The improvements to sort-based API’s in Enum made sorting by date a much beautiful and clean solution.

Lesson learned

In Elixir, as well as in programming in general, the quick solutions don’t always have the expected result and we have to carefully test each solution to make sure it behaves as expected.

Thank you for reading!

Thank you so much for reading, it means a lot to us! Don’t forget to follow Coletiv on Facebook, Twitter, and LinkedIn as we keep posting interesting articles on technologies, processes, and experiences.


If you'd like to work with us on a digital product just drop us a message here.




Do you want to become part of our community?

Join our newsletter 😎