Skip to content

Instantly share code, notes, and snippets.

@westc
Last active November 17, 2023 02:35
Show Gist options
  • Save westc/622f301ce4a121c327504616364d8741 to your computer and use it in GitHub Desktop.
Save westc/622f301ce4a121c327504616364d8741 to your computer and use it in GitHub Desktop.
Sorts an array of values using the criteria specified in a function. This is similar to the Python sort function in that `criteria` is run once for each value and then the return is used to sort the original values. Quick sort is used to sort the values.
/**
* Sorts an array of values using the criteria specified in a function. This is
* similar to the Python sort function in that `criteria` is run once for each
* value and then the return is used to sort the original values. Quick sort is
* used to sort the values.
* @template T
* @param {T[]} values
* The array of values that will be directly sorted and returned.
* @param {(value:T,index:number,values:T[])=>any} criteria
* A function that will return a value for each value in `values` indicating
* how they should be sorted. If this returns an array it will be traversed
* and the underlying values will be compared.
* @param {?boolean=} reverse
* Optional, defaults to `false`. If `true`, `values` will be sorted in
* reverse order.
* @returns {T[]}
* The sorted `values` array.
*/
function sort(values, criteria, reverse) {
reverse = !!reverse;
const ordering = [];
for (let i = 0, l = values.length; i < l; i++) {
const criteriaOutput = criteria(values[i], i, values);
ordering.push(Array.isArray(criteriaOutput) ? criteriaOutput : [criteriaOutput]);
}
// Does a quick sort to sort the values.
const segments = [{start: 0, end: ordering.length - 1}];
for (let segment; segment = segments.shift();) {
let {start, end} = segment;
if (start < end) {
let moveToIndex = start;
const pivot = ordering[end];
for (let index = start, lastIndex = end - 1; index <= lastIndex; index++) {
const value = ordering[index];
// Defines compareResult by comparing the values in value to those in pivot.
const valueLen = value.length;
const pivotLen = pivot.length;
let compareResult = valueLen - pivotLen;
for (let i = 0; i < valueLen && i < pivotLen; i++) {
let valueValue = value[i];
let pivotValue = pivot[i];
if (valueValue !== pivotValue && (valueValue === valueValue || pivotValue === pivotValue)) {
compareResult = valueValue < pivotValue ? -1 : 1;
break;
}
}
// If value should appear before the pivot...
if (reverse ? compareResult > -1 : (compareResult < 1)) {
if (index > start) {
[ordering[moveToIndex], ordering[index]] = [ordering[index], ordering[moveToIndex]];
[values[moveToIndex], values[index]] = [values[index], values[moveToIndex]];
}
moveToIndex++;
}
}
if (moveToIndex < end) {
ordering.splice(moveToIndex, 0, ordering.splice(end, 1)[0]);
values.splice(moveToIndex, 0, values.splice(end, 1)[0]);
}
segments.push({start, end: moveToIndex - 1});
segments.push({start: moveToIndex + 1, end});
}
}
return values;
}
function negate(value) {
return ('string' === typeof value)
? value.replace(/[^]/g, c => String.fromCharCode(0x10000 - c.charCodeAt(0)))
: -value;
}
const people = [
{firstName: 'Zion', lastName: 'Zani'},
{firstName: 'Gwendolyne', lastName: 'Smith'},
{firstName: 'Chris', lastName: 'Roberts'},
{firstName: 'John', lastName: 'Jacob'},
{firstName: 'Gwendolyne', lastName: 'Cares'},
{firstName: 'Zack', lastName: 'Cares'},
{firstName: 'Alison', lastName: 'Cares'},
{firstName: 'John', lastName: 'Smith'},
];
// firstName ASC, lastName ASC
console.log(sort([...people], ({firstName, lastName}) => [firstName, lastName]));
// lastName ASC, firstName ASC
console.log(sort([...people], ({firstName, lastName}) => [lastName, firstName]));
// firstName DESC, lastName DESC
console.log(sort([...people], ({firstName, lastName}) => [firstName, lastName], true));
// lastName DESC, firstName DESC
console.log(sort([...people], ({firstName, lastName}) => [lastName, firstName], true));
// lastName DESC, firstName ASC
console.log(sort([...people], ({firstName, lastName}) => [negate(lastName), firstName]));
@westc
Copy link
Author

westc commented Nov 17, 2023

@westc you should make a version of this in Apex and call it sortUsing():

public static Object[] sortUsing(Object[] values, List<Object[]> criteria) {
    // ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment