Last active
June 17, 2024 15:03
-
-
Save lac5/e10100d54e2b34f32b5d577727964134 to your computer and use it in GitHub Desktop.
Performs a natural sort based on two variables' string values.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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