Skip to main content

Сортування масиву чисел

Щоб відсортувати числа за зростанням, викликайте 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).

Швидкий приклад

javascript
const numbers = [10, 1, 3, 20]; numbers.sort((a, b) => a - b); console.log(numbers); // [1, 3, 10, 20]

Помилка новачків: sort() без компаратора

javascript
const numbers = [10, 1, 3, 20]; numbers.sort(); console.log(numbers); // [1, 10, 20, 3], string sorting

За замовчуванням sort() приводить елементи до рядків і сортує їх у лексикографічному порядку, тобто посимвольно за кодами UTF-16. Саме тому "10" йде перед "2": перший символ "1" менший за "2", і решта цифр уже не має значення.

Важливо: сам масив при цьому не перетворюється на масив рядків, у ньому лишаються числа. До рядків зводяться лише ключі порівняння, тому в консолі ви бачите числа, просто розставлені «за алфавітом».

Правильний спосіб: функція порівняння

javascript
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() стабільний: елементи, для яких компаратор повернув нуль, зберігають вихідний взаємний порядок. Це дозволяє сортувати в кілька проходів, наприклад спочатку за ім'ям, потім за віком.

Спадання і сортування без мутації

Сортування за спаданням, це той самий компаратор навпаки:

javascript
const numbers = [1, 3, 10, 20]; numbers.sort((a, b) => b - a); console.log(numbers); // [20, 10, 3, 1]

Якщо вихідний масив чіпати не можна, сортуйте копію:

javascript
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

У сучасних рушіях є готовий немутуючий метод:

javascript
const sorted = arr.toSorted((a, b) => a - b); // ES2023, returns a new array

Це особливо важливо в React і подібних бібліотеках: state.items.sort(...) змінить той самий об'єкт, посилання не зміниться і перерендеру не буде.

Сусідні випадки: рядки та об'єкти

Той самий принцип працює для будь-яких даних, змінюється лише компаратор:

javascript
// 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 або втратить точність, тому там пишуть явне порівняння:

javascript
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 з потрібною локаллю.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.