Many languages contain special method for ordering which very useful for sorting or data structures with predicates like heap, priority queue, LRU cache and etc.
Problem
Sometimes implement ord
or cmp
/ compare
properly in user space is quite hard. Best example is numbers.
Proper ordering (baseline):
Float64Array.of(-1, -0, NaN, 1, -Infinity, +0, Infinity).sort()
// > [-Infinity, -1, -0, 0, 1, Infinity, NaN]
But implement same ordering with ordinal Array's sort is quite difficult:
[-1, -0, NaN, 1, -Infinity, +0, Infinity].sort()
// > [-1, -Infinity, -0, 0, 1, Infinity, NaN] - expected wrong ordering
[-1, -0, NaN, 1, -Infinity, +0, Infinity].sort((a, b) => a - b) // most popular
// > [-1, -0, NaN, -Infinity, 0, 1, Infinity] - oh, thats still wrong!
// try to be more clever!
[-1, -0, NaN, 1, -Infinity, +0, Infinity].sort((a, b) => Object.is(a, b) ? 0 : (a > b) - (a < b))
// > [-1, -0, NaN, -Infinity, 0, 1, Infinity] - hmm, still wrong :-(
[-1, -0, NaN, 1, -Infinity, +0, Infinity].sort((a, b) => isNaN(a) ? 1 : isNaN(b) ? -1 : a - b)
// > [-Infinity, -1, -0, 0, 1, Infinity, NaN] - at last!
but wait...
[-1, -0, NaN, 1, -Infinity, +0, Infinity, -0].sort((a, b) => isNaN(a) ? 1 : isNaN(b) ? -1 : a - b)
// > [-Infinity, -1, -0, 0, -0, 1, Infinity, NaN] // still wrong!
// and finally
[-1, -0, NaN, 1, -Infinity, +0, Infinity, -0].sort((a, b) => isNaN(a) ? 1 : isNaN(b) ? -1 : Object.is(a - b, -0) ? -1 : a - b)
// > [-Infinity, -1, -0, -0, 0, 1, Infinity, NaN]
Next problem is compare two Date objects! moment
and temporal-proposal already have such special methods for comparison like Instant.compare(d1, d2)
, DateTime.compare(d1, d2)
and etc.
Another example is sorting two objects by key string. Simple example:
[{ key: 'a2' }, { key: 'a1' }].sort((a, b) => (a.key > b.key) - (a.key < b.key));
// or
[{ key: 'a2' }, { key: 'a1' }].sort((a, b) => a.key > b.key ? 1 : a.key < b.key ? -1 : 0));
In any case it's suboptimal due to required 2 string comparisons which may be expensive. Also it looks quite inexpressive. Same for BigInt.compare
which could be much faster than a - b
or (a > b) - (a < b)
approaches.
Solution
I suggest add for every javasript's plain primitive (except Boolean
and Symbol
) ord
or compare
class method which return -1
when a < b
, +1
when a > b
and 0
when a == b
(with proper handling NaNs for numbers):
Number.compare(a, b)
String.compare(a, b)
Date.compare(a, b)
BigInt.compare(a, b)
That's it. No generic comparison or mixed type comparisons.
Examples
[-1, -0, NaN, 1, -Infinity, +0, Infinity].sort(Number.compare)
// > [-Infinity, -1, -0, 0, 1, Infinity, NaN]
[{ key: 'a2' }, { key: 'a1' }].sort((a, b) => String.compare(a.key, b.key)); // ascending order
// > [{ key: 'a1' }, { key: 'a2' }]
[{ key: 'a1' }, { key: 'a2' }].sort((a, b) => -String.compare(a.key, b.key)); // descending order
// > [{ key: 'a2' }, { key: 'a1' }]