Без компаратора sort() перетворює елементи на рядки і порівнює їх за кодами символів UTF-16:
[10, 9, 1].sort(); // [1, 10, 9] - '10' < '9', бо '1' < '9'
Для чисел потрібна функція порівняння, що повертає від'ємне, нуль чи додатне число:
[10, 9, 1].sort((a, b) => a - b); // [1, 9, 10]
users.sort((a, b) => b.age - a.age); // за спаданням віку
Українські рядки. Порівняння за кодами UTF-16 ламає абетку: літери є, і, ї, ґ мають коди більші за я, бо в Unicode стоять поза основним блоком:
['яблуко', 'їжак', 'ґанок', 'гора', 'єнот'].sort();
// ['гора', 'яблуко', 'єнот', 'їжак', 'ґанок'] - неправильно
Правильно - Intl.Collator з українською локаллю:
const collator = new Intl.Collator('uk');
words.sort(collator.compare);
// ['гора', 'ґанок', 'єнот', 'їжак', 'яблуко']
users.sort((a, b) => collator.compare(a.name, b.name));
Корисні опції колатора:
numeric: true- «природне» сортування чисел у рядках:файл2передфайл10;sensitivity: 'base'- ігнорувати регістр і діакритику при порівнянні (зручно для пошуку збігів);caseFirst: 'upper'- великі літери першими.
a.localeCompare(b, 'uk') дає той самий результат, але створює колатор при кожному виклику. Для сортування великих масивів один Intl.Collator значно швидший.
Інші факти про sort:
- сортування змінює масив на місці і повертає той самий масив. Для копії -
toSorted()(ES2023); - сортування стабільне (гарантовано з ES2019): елементи з однаковим ключем зберігають взаємний порядок. Тому сортування за кількома ключами можна робити послідовно, але простіше одним компаратором:
items.sort((a, b) => a.category.localeCompare(b.category, 'uk') || b.price - a.price);
- компаратор має бути узгодженим: повертати
true/false((a, b) => a > b) - помилка. Результат залежить від рушія і може бути неправильним; undefinedзавжди йдуть у кінець, компаратор для них не викликається.
Сортування на сервері й на клієнті має збігатися. Якщо список посторінково сортує база (з колацією uk), а клієнт досортовує сторінку через sort() без колатора, порядок «стрибатиме».