Сортування масиву чисел
Щоб відсортувати числа за зростанням, викликайте sort() з функцією порівняння: arr.sort((a, b) => a - b). Без компаратора метод приводить елементи до рядків і шикує їх за алфавітом, тому число 10 опиняється перед 3, і це найпоширеніша помилка на співбесідах.
Теорія
TL;DR
- За зростанням:
arr.sort((a, b) => a - b). - За спаданням:
arr.sort((a, b) => b - a). - Без компаратора
sort()порівнює рядкові представлення елементів. - Компаратор повертає від'ємне, додатне або нуль, і саме цим задає порядок.
sort()мутує вихідний масив і повертає посилання на нього.- Немутуючий варіант:
[...arr].sort(cmp)абоarr.toSorted(cmp)(ES2023).
Швидкий приклад
const numbers = [10, 1, 3, 20];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 3, 10, 20]Помилка новачків: sort() без компаратора
const numbers = [10, 1, 3, 20];
numbers.sort();
console.log(numbers); // [1, 10, 20, 3], string sortingЗа замовчуванням sort() приводить елементи до рядків і сортує їх у лексикографічному порядку, тобто посимвольно за кодами UTF-16. Саме тому "10" йде перед "2": перший символ "1" менший за "2", і решта цифр уже не має значення.
Важливо: сам масив при цьому не перетворюється на масив рядків, у ньому лишаються числа. До рядків зводяться лише ключі порівняння, тому в консолі ви бачите числа, просто розставлені «за алфавітом».
Правильний спосіб: функція порівняння
const numbers = [10, 1, 3, 20];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 3, 10, 20]Функція (a, b) => a - b каже рушію, як порівнювати числа:
- якщо
a - bменше за 0, тоaйде раніше; - якщо
a - bбільше за 0, то раніше йдеb; - якщо
a - bдорівнює 0, порядок не змінюється.
| Значення компаратора | Що означає |
|---|---|
| Від'ємне | a ставимо перед b |
| Додатне | b ставимо перед a |
| Нуль | елементи рівні, порядок зберігається |
Починаючи з ES2019, sort() стабільний: елементи, для яких компаратор повернув нуль, зберігають вихідний взаємний порядок. Це дозволяє сортувати в кілька проходів, наприклад спочатку за ім'ям, потім за віком.
Спадання і сортування без мутації
Сортування за спаданням, це той самий компаратор навпаки:
const numbers = [1, 3, 10, 20];
numbers.sort((a, b) => b - a);
console.log(numbers); // [20, 10, 3, 1]Якщо вихідний масив чіпати не можна, сортуйте копію:
const arr = [5, 2, 9, 1];
const sorted = [...arr].sort((a, b) => a - b);
console.log(sorted); // [1, 2, 5, 9]
console.log(arr); // [5, 2, 9, 1], the original is untouchedУ сучасних рушіях є готовий немутуючий метод:
const sorted = arr.toSorted((a, b) => a - b); // ES2023, returns a new arrayЦе особливо важливо в React і подібних бібліотеках: state.items.sort(...) змінить той самий об'єкт, посилання не зміниться і перерендеру не буде.
Сусідні випадки: рядки та об'єкти
Той самий принцип працює для будь-яких даних, змінюється лише компаратор:
// numbers as strings: convert before comparing
['10', '9', '2'].sort((a, b) => Number(a) - Number(b)); // ['2', '9', '10']
// text with correct alphabetical order
['яблуко', 'абрикос', 'їжа'].sort((a, b) => a.localeCompare(b, 'uk'));
// objects by field
const users = [{ name: 'Bob', age: 31 }, { name: 'Alice', age: 25 }];
users.sort((a, b) => a.age - b.age); // Alice firstДля великих чисел, що не влазять у Number, віднімання поверне BigInt або втратить точність, тому там пишуть явне порівняння:
arr.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));Коротко
| Задача | Код | Результат |
|---|---|---|
| За зростанням | arr.sort((a, b) => a - b) | [1, 2, 3, 4] |
| За спаданням | arr.sort((a, b) => b - a) | [4, 3, 2, 1] |
| Без зміни вихідного масиву | [...arr].sort(...) | новий відсортований масив |
| Без зміни, сучасний синтаксис | arr.toSorted(...) | новий відсортований масив |
Підсумок: щоб відсортувати числа за зростанням, завжди передавайте функцію порівняння:
arr.sort((a, b) => a - b).
Типові помилки
- Викликати
sort()без аргументів для чисел. Це лексикографічне сортування, і на масивах, де є і однозначні, і двозначні числа, результат майже завжди неправильний. - Повертати з компаратора
trueабоfalse. Наприкладarr.sort((a, b) => a > b): булеве значення зводиться до 1 або 0, «менше» рушій ніколи не отримує, і порядок виходить непередбачуваним. Компаратор має повертати число зі знаком. - Забувати, що
sort()мутує. Метод змінює вихідний масив і повертає його ж, а не копію. Для чужих даних, props і стану спершу копіюйте. - Сортувати масив з
NaN. Будь-яке віднімання зNaNдаєNaN, компаратор фактично повертає «невідомо», і порядок стає сміттєвим. Такі значення треба відфільтрувати заздалегідь. - Не враховувати
undefinedі дірки. Значенняundefinedзавжди йдуть у кінець, а порожні позиції розрідженого масиву ще далі, і компаратор для них не викликається. - Порівнювати текст через
a - bабоa > b. Для рядків з літерами поза ASCII правильний порядок даєlocaleCompareз потрібною локаллю.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.