I imagine there is an easy way to do this with Enum but I am struggling to piece it together.
I have a keyword list with keys in some random order. I would like to order the keyword list based on a list of keys that I have in the right order. How would I sort based on the keys?
Since list are ordered and you have one with the proper order, i’d go with a more direct version, like this. Note that i assume that the ordered list only contains available keys.
Anything that is internally based on Keyword.get or Keyword.fetch will silently discard any duplicated keys. At most you’d want to use Keyword.get_values but you’d have to reconstruct the individual entries.
Marcus’ suggestion will preserve duplicates correctly but could be optimized further with memoization of the indexing, which could loosely be something like order_of_keys |> Enum.with_index() |> Map.new(). Then sort_by switches to Map.fetch inside.
Thank you, in my particular application I know I have no duplicates the and keys are the same so it was pretty easy to use comprehension to build up from the ordered listed. However I can see in most applications the danger so its good to know how to do it safely as you suggest. Enum.with_index is awesome, I remember reading about it but haven’t had a use yet. Thanks for the suggestion!
defmodule KW do
def take_in_order(kw, keys)
when is_list(kw) and is_list(keys) do
keys
|> Enum.reduce({[], kw}, fn key, {result, starting_kw} ->
{values, next_kw} = Keyword.pop(starting_kw, key)
{[{key, values} | result], next_kw}
end)
|> elem(0)
|> Enum.reverse()
end
end
This basically iterates over the keys and gradually prepends each key/value pair to the result – while gradually taking values from a constantly shrinking keyword list – until we finally take only the first element of the resulting accumulator tuple (the result in a reversed order) which we reverse to get the desired result.
This code assumes no duplicated keys (and you said your input will conform to this rule).