← Download MyJS
MyJS block
order 1.0.0
Orders a copy of an array by one or more criteria. The first criterion decides the order, the next one breaks ties, and so on. Items that tie on every criterion keep their original order. Each getter is called once per item.
- Exports
-
order - Works in
- Browser and Node.js
The code of version 1.0.0
/**
* Gets the value to order an item by.
*
* @template T
* @callback order__Getter
* @param {T} item
* The item being ordered.
* @param {number} index
* The item's index in the original array.
* @param {T[]} array
* The original array.
* @param {number} criterionIndex
* The index of the criterion this getter belongs to.
* @returns {number|string|bigint|boolean|Date|null|undefined}
* A value that can be compared with `<` and `>`. `undefined`, `null` and
* `NaN` always go last, even when the criterion is reversed.
*/
/**
* A criterion as an object, to order by it in reverse.
*
* @template T
* @typedef {Object} order__CriterionObject
* @property {order__Getter<T>} getter
* Gets the value to order each item by.
* @property {boolean} [reverse=false]
* `true` to order from the largest value to the smallest.
*/
/**
* Orders a copy of an array by one or more criteria. The first criterion
* decides the order, the next one breaks ties, and so on. Items that tie
* on every criterion keep their original order. Each getter is called once
* per item.
*
* @template T
* @param {T[]} array
* The array to order. It isn't changed.
* @param {...(order__Getter<T>|order__CriterionObject<T>)} criteria
* The criteria, most important first. A getter function orders from the
* smallest value to the largest; use `{ getter, reverse: true }` to order
* from the largest to the smallest.
* @returns {T[]}
* A new array with the items in order.
* @throws {TypeError}
* If a criterion isn't a function or an object with a `getter` function.
*
* @example
* const people = [
* { name: 'Cy', age: 30 },
* { name: 'Al', age: 25 },
* { name: 'Bo', age: 30 }
* ];
* // Oldest first, then by name
* order(people, { getter: p => p.age, reverse: true }, p => p.name);
* // -> [{ name: 'Bo', age: 30 }, { name: 'Cy', age: 30 }, { name: 'Al', age: 25 }]
*/
function order(array, ...criteria) {
criteria = criteria.map(criterion => {
criterion = typeof criterion === 'function' ? { getter: criterion } : Object(criterion);
if (typeof criterion.getter !== 'function') {
throw new TypeError('Each criterion must be a function or an object with a getter function.');
}
return { getter: criterion.getter, reverse: !!criterion.reverse };
});
// undefined, null and NaN can't be compared with < and >, so they go last
const isMissing = value => value == null || value !== value;
return array
.map((item, index) => ({
item,
values: criteria.map(({ getter }, criterionIndex) => getter(item, index, array, criterionIndex))
}))
.sort((a, b) => {
for (let i = 0; i < criteria.length; i++) {
const x = a.values[i], y = b.values[i];
const xMissing = isMissing(x), yMissing = isMissing(y);
if (xMissing || yMissing) {
if (xMissing !== yMissing) {
return xMissing ? 1 : -1;
}
}
else if (x < y) {
return criteria[i].reverse ? 1 : -1;
}
else if (x > y) {
return criteria[i].reverse ? -1 : 1;
}
}
return 0;
})
.map(entry => entry.item);
}
Versions
-
1.0.0 Oct 17, 2026
Initial