In JavaScript, things get interesting when you need to sort strings that contain numbers in a way that matches human expectations.
const files = ['IMG_2.png', 'IMG_1.png', 'IMG_10.png', 'IMG_20.png'];
files.sort();
// Output: ['IMG_1.png', 'IMG_10.png', 'IMG_2.png', 'IMG_20.png']
This happens because, by default, when no compare function is provided, JavaScript’s array sort() method converts the elements into strings, then compares their sequences of UTF-16 code unit values.
This behaviour is well documented on MDN.
So, how do we sort arrays like the ones above in a more accurate, human-friendly order?
This is where the compare() method of the Intl.Collator API with the numeric: true option shines. It provides the natural sorting behaviour that correctly handles numbers alongside other characters.
When numeric: true is set, the collator detects numeric substrings inside the strings and parses those substrings as actual numbers, not as sequences of digits. And then it compares the numbers numerically, not character by character.
const naturalOrder = new Intl.Collator(undefined, {
numeric: true,
}).compare;
const files = ['IMG_2.png', 'IMG_1.png', 'IMG_10.png', 'IMG_20.png'];
files.sort((a, b) => naturalOrder(a, b));
// Output: ['IMG_1.png', 'IMG_2.png', 'IMG_10.png', 'IMG_20.png']
Hat tip to Jan Miksovsky, whose zero dependencies SSG project led me to discover this.