Skip to content

Instantly share code, notes, and snippets.

@lac5
Last active June 17, 2024 15:03
Show Gist options
  • Save lac5/e10100d54e2b34f32b5d577727964134 to your computer and use it in GitHub Desktop.
Save lac5/e10100d54e2b34f32b5d577727964134 to your computer and use it in GitHub Desktop.
Performs a natural sort based on two variables' string values.
/**
* Performs a natural sort based on two variables' string values.
* Example:
* ['C12', 'B', 'c2', 'a', 'c1'].sort(natSort); //=> ['a', 'B', 'c1', 'c2', 'C12']
*
* [{name: 'Bob'}, {name: 'Zack'}, {name: 'Alex'}].sort(function(a, b) {
* return natSort(a, b, 'name', true);
* }); //=> [{name: 'Alex'}, {name: 'Bob'}, {name: 'Zack'}]
*
* @param {any} a
* @param {any} b
* @param {string} [key]
* @param {boolean} [reverse = false]
* @return {number}
*/
export default function natSort(a, b, key, reverse = false) {
/** @type {number} */
let sign = reverse ? -1 : 1;
if (key) {
a = a[key];
b = b[key];
}
/** @type {string} */
let aStr = String(a);
if (a.length === 0) return -sign;
/** @type {string} */
let bStr = String(b);
if (b.length === 0) return sign;
/** @type {number} */
let i = 0;
// Split a and b based on number and non-number sets.
/** @type {string[]} */
let aMatch = aStr.match(/\d+|\D+/g) || [];
/** @type {string[]} */
let bMatch = bStr.match(/\d+|\D+/g) || [];
/** @type {number} */
let length = Math.min(aMatch.length, bMatch.length);
if (length <= 0) return 0;
// Find which set is different.
while (aMatch[i].toLowerCase() === bMatch[i].toLowerCase()) {
i += 1;
if (i >= length) {
// At this point, a and b must be the same up to a certain point.
// The shorter one should be before the longer one.
return (aStr.length - bStr.length) * sign;
}
}
// Use math or compare string values.
if (/^\d+$/.test(aMatch[i]) && /^\d+$/.test(bMatch[i])) {
return (Number(aMatch[i]) - Number(bMatch[i])) * sign;
}
return (aMatch[i].toLowerCase() > bMatch[i].toLowerCase() ? 1 : -1) * sign;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment