Ordered sorting of a map to a keyword list

Just wondered if there is some standard library function for converting a map to a keyword list, but sorting it on the way.

something like;

map = %{"z" => 4, "y" => 6, "x" => 7}
order = ["x", "y"]
output = new_func(map, order)
# output now equals [{"x", 7}, {"y", 6}]

Here’s one approach:

iex(1)> map = %{"z" => 4, "y" => 6, "x" => 7}
%{"x" => 7, "y" => 6, "z" => 4}
iex(2)> order = ["x", "y"]
["x", "y"]
iex(3)> Enum.map(order, fn key -> {key, Map.get(map, key)} end)
[{"x", 7}, {"y", 6}]
5 Likes

No the stdlib does not have that and you’ll see exactly why: it’s easy to do it yourself.

%{"z" => 4, "y" => 6, "x" => 7}
|> Map.take(["x", "y"])   # only take keys "x" and "y"
|> Map.to_list()          # convert the map to a list of tuples
|> Enum.sort_by(fn {key, _value} -> key end) # sort the list of tuples by their first element

And you will get [{"x", 7}, {"y", 6}] as your result.

2 Likes

You don’t need to convert the map to a list because maps are enumerable.

I said something similar 3 years ago :older_man: (actually @mudasobwa said it and I got the points. Thanks :chart_with_upwards_trend::sparkles:).

1 Like

I keep forgetting. :smiley:

I just like convenience functions, like how a map has keys() and values() when you could implement them yourself

Sure, you can just put it in your project. Two minute job.

1 Like

That’s super inefficient though. If you had e.g. a map with 20 elements and wanted 10 of them, that solution in SO would do 10 map lookups.

Saving a line or two of code just to make the complexity of the code something almost O(n^2) is a bad practice. Whereas the Map.take + Enum.sort_by is not even two full passes of the underlying list.

You sure about that?

sort_by/3 calculates the comparison value for each element in the enumerable once instead of once for each element in each comparison. This implies sort_by/3 must do an initial pass on the data to compute those values.

However, if those values are cheap to compute, for example, you have already extracted the field you want to sort by into a tuple, then those extra passes become overhead. In such cases, consider using List.keysort/3 instead

https://hexdocs.pm/elixir/Enum.html#sort_by/3-performance-characteristics

Well I am not in the mood to compose a benchmark project but if you want to, go crazy.

I didn’t benchmark it, but it’s nlogn, which is about par for the course for sorting in immutable languages AFAIK. Premature optimisation, etc…

Let’s not discuss further if nobody’s willing to actually run the numbers :joy: