Enum.sort_by to sort a record set (i.e. a list of maps) based on 2 "columns"

I found José’s useful response to how to sort a list of maps by a column. I’m not following how to expand this into sorting by multiple columns.

For example, given the following “record set”:

records = [
    %{age: 6, name: "Apple"},
    %{age: 4, name: "Boy"},
    %{age: 1, name: "Cat"},
    %{age: 0, name: "Dog"}
]

I can sort by age descending doing something like this:

Enum.sort_by(records, fn(r) -> r[:age] end, &>=/2)

However, what if I want to sort by :name when there is a tie? Something like how a SQL query would do ORDER BY age DESC, name ASC.

Thanks for any pointers!

1 Like
Enum.sort_by(records, fn(r) -> {r[:age], r[:name]} end, &>=/2)

Tuples, when compared with one another, are ordered according to what appears first in the tuple. So in this case it would sort by age first, then by name. Doing it by different directions for each though is a bit harder.

2 Likes
Interactive Elixir (1.9.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> data = [
...(1)>     %{age: 6, name: "Apple"},
...(1)>     %{age: 4, name: "Dog"},
...(1)>     %{age: 1, name: "Cat"},
...(1)>     %{age: 4, name: "Boy"}
...(1)> ]
[
  %{age: 6, name: "Apple"},
  %{age: 4, name: "Dog"},
  %{age: 1, name: "Cat"},
  %{age: 4, name: "Boy"}
]
iex(2)> compare =
...(2)>   fn
...(2)>     x, x -> :eq
...(2)>     x, y when x > y -> :gt
...(2)>     _, _ -> :lt
...(2)>   end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(3)> age_desc =
...(3)>   fn lhs, rhs ->
...(3)>     case compare.(lhs[:age], rhs[:age]) do
...(3)>       :lt -> false
...(3)>       _ -> true
...(3)>     end
...(3)>   end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(4)> Enum.sort(data, age_desc)
[
  %{age: 6, name: "Apple"},
  %{age: 4, name: "Dog"},
  %{age: 4, name: "Boy"},
  %{age: 1, name: "Cat"}
]
iex(5)> age_desc_name_asc =
...(5)>   fn lhs, rhs ->
...(5)>     case {compare.(lhs[:age],rhs[:age]), compare.(lhs[:name],rhs[:name])} do
...(5)>       {:lt, _} -> false
...(5)>       {:eq, :gt} -> false
...(5)>       {_,_} -> true
...(5)>     end
...(5)>   end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(6)> Enum.sort(data, age_desc_name_asc)
[
  %{age: 6, name: "Apple"},
  %{age: 4, name: "Boy"},
  %{age: 4, name: "Dog"},
  %{age: 1, name: "Cat"}
]
iex(7)> 
7 Likes

Wow, that is crazy thorough. Thank you for the example!