Сортировка
Последний работник у нашего конвейера — сортировщик. Он берёт два ящика, сравнивает их и решает, какой поставить раньше. Так, пара за парой, он расставляет по порядку всю ленту.
Метод sort и есть такой сортировщик: он расставляет элементы массива по порядку. Со словами он работает так, как ожидается, а с числами — нет:
const words = ["pear", "apple", "fig"];
words.sort();
console.log(words); // [ 'apple', 'fig', 'pear' ]
const nums = [25, 100, 3];
nums.sort();
console.log(nums); // [ 100, 25, 3 ]Давайте разберём, что произошло:
- Слова встали по алфавиту:
apple,fig,pear. - Числа встали в странном порядке:
100оказалось первым, хотя оно самое большое.
Почему так вышло с числами, часто спрашивают на собеседовании. Ответ — в том, как sort сравнивает элементы без подсказки.
Сравнение как строк
Без аргумента sort переводит каждый элемент в строку и сравнивает строки символ за символом, как слова в словаре. Строка "100" начинается с "1", а "1" меньше "2" и "3". Поэтому 100 встаёт первым, хотя как число оно самое большое.
Строка "10" тоже меньше строки "9": "10" < "9" даёт true. Строки сравниваются по кодам символов, поэтому заглавные буквы идут раньше строчных: ["b", "A", "a"].sort() даёт [ 'A', 'a', 'b' ].
Функция сравнения
Чтобы сравнивать числа как числа, сортировщику дают правило. В sort передают функцию сравнения. sort вызывает её с двумя элементами a и b и смотрит на знак результата:
- отрицательное число —
aвстаёт раньшеb; - положительное число —
bвстаёт раньшеa; 0— порядок этих двух элементов не меняется.
Разность a - b отрицательна, когда a меньше. Поэтому (a, b) => a - b расставляет числа по возрастанию, а (a, b) => b - a — по убыванию:
nums.sort((a, b) => a - b);
console.log(nums.join(" ")); // 3 25 100
nums.sort((a, b) => b - a);
console.log(nums.join(" ")); // 100 25 3Возьмём пару 25 и 3 при сортировке по возрастанию. Разность 25 - 3 равна 22, это положительное число, поэтому 3 встаёт раньше 25.
Сравнивать можно что угодно, например длину строк: words.sort((a, b) => a.length - b.length).
Функция сравнения должна возвращать число: отрицательное, положительное или 0. Функция (a, b) => a > b возвращает true или false. sort читает их как 1 и 0, и отрицательного числа не получает никогда. С ней порядок бывает неверным: в Node.js [3, 1, 2] так и остаётся [3, 1, 2].
sort и toSorted
Здесь аналогия с сортировщиком перестаёт работать. Настоящий сортировщик мог бы выставить ящики на новую ленту, а sort новый массив не создаёт. Он переставляет элементы того массива, у которого вызван.
sort меняет массив
sort меняет массив на месте, как push и pop. sort возвращает тот же самый массив, а не копию.
toSorted возвращает копию
Метод toSorted принимает ту же функцию сравнения, но возвращает отсортированную копию, а исходный массив не трогает. Метод появился в языке в 2023 году; его знают Node.js 22 и браузеры, выпущенные после середины 2023 года:
const scores = [70, 95, 80];
const ranked = scores.toSorted((a, b) => b - a);
console.log(ranked.join(" ")); // 95 80 70
console.log(scores.join(" ")); // 70 95 80В ranked баллы стоят по убыванию, а в scores остался исходный порядок.
Частая ошибка
Результат sort сохранили в новую константу и ждали, что исходный массив останется прежним:
const prices = [30, 10, 20];
const sorted = prices.sort((a, b) => a - b); // ✗
console.log(prices.join(" "));Ошибки нет, но программа выводит 10 20 30: sort переставил элементы самого prices. sorted и prices — два имени одного массива, и sorted === prices даёт true. Когда исходный порядок нужен дальше, пишите prices.toSorted((a, b) => a - b) или сортируйте копию [...prices].
Главное
- Без аргумента
sortсравнивает элементы как строки, поэтому100встаёт раньше25. - Для чисел передают функцию сравнения:
(a, b) => a - bпо возрастанию,(a, b) => b - aпо убыванию. sortменяет сам массив и возвращает его же, аtoSortedвозвращает новый массив.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.