Sorting an Array of Maps

Using an Array to hold similar Maps is a common data pattern in KRL.

When the Maps need to be in some particular order in the Array, it would be natural to use the sort() operator (see Array operators).

This operator works as expected with Arrays of Numbers and Arrays of Strings. It takes one argument, which can be used to specify different sorts of numeric and string comparisons. The trick for sorting Arrays of Maps is to use a function in that position.

Pass the sort operator a function

To sort an Array of similar Maps by sorting on the value of a specific key, you can define a function like this:

by = function(key){ function(a,b){a{key}.encode() cmp b{key}.encode()} }

which returns a function that compares the values of the same key in two different maps. It satisfies the requirements for a function used within the sort() operator. The specific key to be used is captured in a closure, and the returned function does the work required, of comparing two arbitrary Maps (a and b) as the sort progresses.

To use this to sort channels by identifier, use something like:

wrangler:channels().sort(by("id"))

where wrangler:channels() returns an Array of Maps, one for each channel defined in the current pico, and this expression produces a new Array of Maps with the channel Maps in order by the id field.

Naming the function by makes the overall expression read in a sensible way, “give me my channels, sorted by identifier”.

Extension for primary and secondary sort

Suppose you want to sort on two different keys, a primary sort and a secondary sort. Then this implementation of by will do the trick:

by = function(key1,key2){ key2 => function(a,b){ a{key1}.encode() cmp b{key1}.encode() || a{key2}.encode() cmp b{key2}.encode() } | function(a,b){ a{key1}.encode() cmp b{key1}.encode() } }

Because || is implemented with short-circuit Boolean evaluation, the test against key2 will happen only if the two maps agree on the value of key1 (i.e. its cmp returns zero).

As an example of use, you might want to sort the pico’s channels first by tags and for channels with matching tags have them in order by the identifier:

This by function can still be called with a single key value, because if key2 is missing, it returns the simpler function.

Extension for key paths

Sometimes the value that is present in all of the Maps in an Array that you want to sort by is deeper in the structure. For example, the ctx built-in module, or library, can give you an Array with a Map for every ruleset installed in your pico. If you want to sort these by the flushed date, you could use this expression:

Note

The cmp infix operator is only defined for Strings. The encode() operator always produces a String and leaves a String unchanged. So, in case the value specified in one of the Maps being sorted isn’t a String, it will be converted into one before being subjected to the cmp comparison. Using encode() makes the function returned by the by function more robust.

Copyright Picolabs | Licensed under Creative Commons.