Skip to content

Instantly share code, notes, and snippets.

@westc
Created March 7, 2023 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westc/c49c9f917c0e002218ba1dc630e6cc56 to your computer and use it in GitHub Desktop.
Save westc/c49c9f917c0e002218ba1dc630e6cc56 to your computer and use it in GitHub Desktop.
getPathsComparer() - Makes it possible to easily compare multiple nested objects. This was designed to easily be used in conjunction with Array.prototype.sort().
/**
* Makes it possible to easily compare multiple nested objects.
* @param {...(number|string|(number|string)[])} paths
* The paths to the values within the 2 arguments passed to the returned
* function. The values at these paths will be compared.
* @returns
* A function which accepts 2 values that will be compared.
*
* @example
* console.log([
* {first: 'Anne', last: 'Mayo', age: 25}, // becomes 2nd
* {first: 'Valeria', last: 'Andrade', age: 26}, // becomes 1st
* {first: 'Jerry', last: 'Mayo', age: 27}, // becomes 3rd
* ].sort(getPathsComparer('last', 'first')));
*
* @example
* console.log([
* {first: 'Greg', last: 'Sica', age: 25}, // becomes 2nd
* {first: 'Lindsay', last: 'Barbados', age: 26}, // becomes 3rd
* {first: 'Corey', last: 'Trent', age: 27}, // becomes 1st
* ].sort(getPathsComparer(['first'], ['last'])));
*
* @example
* console.log([
* {first: 'Greg', last: 'Sica', grades: ['B', 'C']}, // becomes 3rd
* {first: 'Lindsay', last: 'Barbados', grades: ['A', 'B']}, // becomes 2nd
* {first: 'Corey', last: 'Trent', grades: ['C', 'A']}, // becomes 1st
* ].sort(getPathsComparer(['grades', -1])));
*/
function getPathsComparer(...paths) {
// Coerce each value within `paths` to an array.
paths = paths.map(p => Array.isArray(p) ? p : [p]);
// The paths comparer function that will be returned.
return function(a, b) {
// Loop through all of the paths.
for (const path of paths) {
// a1 and b1 will represent a and b or one of their descendants.
let a1 = a, b1 = b;
// Loop through each key of each path.
for (const key of path) {
// If a1 or b1 is null or undefined then break out.
if (a1 == null || b1 == null) break;
// If the key is a negative number count from the end of the array-like
// object to get the next values for a1 and b1.
if (key < 0 && 'number' === typeof key) {
a1 = a1[a1.length + key];
b1 = b1[b1.length + key];
}
// In all other cases just pull the value for `key`.
else {
a1 = a1[key];
b1 = b1[key];
}
}
// Do a comparison of a1 to b1 and if they are not the same return either
// -1 or 1.
const result = (a1 == null || b1 == null)
? a1 == b1 ? 0 : a1 == null ? -1 : 1
: (a1 === b1 || (a1 !== a1 && b1 !== b1)) ? 0 : a1 < b1 ? -1 : 1;
if (result) return result;
}
return 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment