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.