/**
* Global collection of commonly used comparison functions.
* These comparators are intended for use in sorted collections such as {@link Queue}.
*
* **Available comparators:**
* - **ASCENDING**: Sorts values from smallest to largest.
* - **DESCENDING**: Sorts values from largest to smallest.
* - **INSERTION**: Disables sorting; preserves insertion order.
* - **SINGULAR**: Treats all values as equivalent — only one unique item is kept.
* - **BY_PROPERTY**: Produces a comparator that sorts objects by one or more specified properties.
* - **REVERSE**: Produces a comparator that inverts another comparator.
*
* @namespace ORDER
* @global
*/
export const ORDER = {
/**
* Sorts primitive values in ascending order.
* @memberof ORDER
*/
ASCENDING: (a, b) =>
a < b ? -1 : (a === b ? 0 : 1),
/**
* Sorts primitive values in descending order.
* @memberof ORDER
*/
DESCENDING: (a, b) =>
a < b ? +1 : (a === b ? 0 : -1),
/**
* No sorting; preserves insertion order.
* @memberof ORDER
*/
INSERTION: undefined,
/**
* Treats all values as equivalent.
* @memberof ORDER
*/
SINGULAR: (a, b) => 0,
/**
* Creates a comparator function that sorts objects by one or more properties.
* Each property can have its own comparator; if fewer comparators than properties
* are provided, the last comparator is reused for the remaining properties.
*
* @param {string | string[]} names - The property name or array of property names to compare.
* @param {Function | Function[]} [comparators=ORDER.ASCENDING] - Comparator function(s) applied to property values.
* Defaults to `ORDER.ASCENDING`. Each comparator should follow the `(a, b) => number` convention.
* @returns {Function} A comparator function `(a, b) => number` that can be used in Array.sort or other sorting utilities.
*
* @example
* // Sort by single property 'age' ascending
* array.sort(ORDER.BY_PROPERTY('age'));
*
* // Sort by multiple properties: first 'age' ascending, then 'name' descending
* array.sort(ORDER.BY_PROPERTY(['age','name'], [ORDER.ASCENDING, ORDER.DESCENDING]));
*
* @memberof ORDER
*/
BY_PROPERTY(names, comparators = ORDER.ASCENDING) {
// Ensure arrays
if (!Array.isArray(names)) names = [names];
if (!Array.isArray(comparators)) comparators = [comparators];
return (a, b) => {
for (let i = 0; i < names.length; i++) {
const name = names[i];
const av = a[name];
const bv = b[name];
const comparator = comparators[i] || comparators[comparators.length - 1];
const cmp = comparator(av, bv);
if (cmp !== 0) return cmp;
}
return 0;
};
},
/**
* Returns a comparator that reverses the order of another comparator.
*
* **Example:**
* `const cmp = ORDER.REVERSE(ORDER.ASCENDING);`
* `items.sort(cmp); // same as DESCENDING`
*
* @param {Function} comparator - Any comparator `(a, b) => number`.
* @returns {Function} A comparator that negates the result of the given comparator.
*
* @memberof ORDER
*/
REVERSE(comparator) {
return (a, b) => -comparator(a, b);
}
};
/**
* Factory functions that create `sorter` functions.
*
* A `sorter(node)` function must return a tuple:
*
* sorter(node) → [keyComparator, valueComparatorOrFlag]
*
* This tuple controls how each node computes:
* - **sortedKeys** — ordering of its child keys
* - **sortedValues** — ordering of values (its equivalence class)
*
* @namespace SORTER
* @global
*/
export const SORTER = {
/**
* Creates a depth-aware `sorter(node)` function.
*
* @param {Function|Array<Function>} keyComparators
* A key comparator or an array of comparators indexed by depth.
* If a single function is provided, it is wrapped into an array.
*
* The comparator chosen for a node is:
* - `keyComparators[node.depth]` if it exists
* - otherwise the **last** comparator in the array
*
* @param {Function|boolean|Array<Function|boolean>} valueComparatorsOrFlags
* Comparator(s) or boolean flag(s) controlling ordering of stored values.
* A single entry is upgraded to an array.
*
* - comparator → values stored in a `SortedArray`
* - boolean → values stored in an `ArrayQueue`
* - `false` → FIFO
* - `true` → LIFO
*
* @returns {function(node): Array<Function, Function|boolean>}
* Returns a function that, for each node, selects:
* - the appropriate **key comparator** based on depth
* - the appropriate **value comparator/flag** based on depth
*
* @memberof SORTER
*
* @example
* const sorter = SORTER.BY_DEPTH([ORDER.ASCENDING, ORDER.DESCENDING], true);
*/
BY_DEPTH: (keyComparators, valueComparatorsOrFlags) => {
if (!Array.isArray(keyComparators)) keyComparators = [keyComparators];
if (!Array.isArray(valueComparatorsOrFlags)) valueComparatorsOrFlags = [valueComparatorsOrFlags];
return node => [
keyComparators[node.depth] ??
keyComparators[keyComparators.length - 1],
valueComparatorsOrFlags[node.depth] ??
valueComparatorsOrFlags[valueComparatorsOrFlags.length - 1]
];
},
/**
* Creates a **uniform** `sorter(node)` function where all nodes,
* at any depth, use the **same** key comparator and the **same**
* value comparator/flag.
*
* This is the opposite of {@link SORTER.BY_DEPTH}.
* Instead of varying by `node.depth`, the sorting strategy is
* globally consistent across the entire classifier.
*
* @param {Function} keyComparator
* Comparator used to order all child keys in every node.
*
* @param {Function|boolean} valueComparatorOrFlag
* Comparator or queue-flag used to order values in all nodes.
*
* - comparator → values stored in a `SortedArray`
* - boolean → values stored in an `ArrayQueue`
* - `false` → FIFO
* - `true` → LIFO
*
* @returns {function(node): Array<Function, Function|boolean>}
* A `sorter(node)` function that always returns the same tuple.
*
* @example
* const sorter = SORTER.UNIFORM(ORDER.ASCENDING, false);
*
* @memberof SORTER
*/
UNIFORM: (keyComparator, valueComparatorOrFlag) => {
return () => [keyComparator, valueComparatorOrFlag];
}
};