Сортировка по возрастанию и убыванию в JavaScript и Excel

Сортировка по возрастанию и убыванию в JavaScript и Excel Полезное

Сортировка — это перестановка элементов набора в заданном порядке: по возрастанию (от меньшего к большему) или по убыванию (от большего к меньшему). Ниже разберем, как сделать это в JavaScript методом sort с функцией сравнения (компаратором) и его иммутабельным аналогом toSorted, а в Excel — формулой СОРТ и кнопками А-Я / Я-А. Отдельно покажем главную ловушку JavaScript: sort без компаратора сортирует числа как строки.

Ловушка: sort без компаратора сортирует числа как текст

Первое, обо что спотыкаются новички: у массива чисел вызывают sort() без аргументов и ждут числовой порядок. Метод Array.prototype.sort по умолчанию приводит элементы к строкам и сравнивает их посимвольно (лексикографически). Поэтому "10" оказывается раньше "2", ведь символ 1 меньше символа 2.

Неверный код:

console.log([1, 2, 10, 21, 100].sort());

Фактический результат (проверено в Node.js 22):

[ 1, 10, 100, 2, 21 ]

Порядок сломан: 10 и 100 встали перед 2. Исправление — передать в sort функцию сравнения (компаратор), которая сравнивает числа как числа:

// по возрастанию: a - b
console.log([1, 2, 10, 21, 100].sort((a, b) => a - b));
[ 1, 2, 10, 21, 100 ]

Правило простое: для чисел компаратор в sort обязателен. Для сортировки строк компаратор не нужен, но лексикографический порядок там как раз уместен (учет регистра и алфавита — отдельная тема, см. ниже про localeCompare).

Как работает компаратор

Компаратор — это функция двух аргументов (a, b), которая возвращает число:

  • отрицательное — a идет раньше b;
  • положительное — a идет позже b;
  • ноль — порядок a и b не меняется относительно друг друга.

Выражение a - b дает отрицательное значение, когда a < b, значит меньшие числа встают в начало — это возрастание. Чтобы получить убывание, достаточно поменять операнды местами:

// по убыванию: b - a
console.log([1, 2, 10, 21, 100].sort((a, b) => b - a));
[ 100, 21, 10, 2, 1 ]

Важно: возвращать нужно любое число нужного знака, а не строго 1, 0 и -1. Приемы вида a - b работают именно потому, что знак разности и есть ответ компаратора. Для строк такой трюк не подходит — вычитать строки нельзя, там применяют localeCompare (о нем ниже).

Мутирующий sort и иммутабельный toSorted

Ключевое различие, о котором забывают: sort меняет исходный массив на месте и возвращает ссылку на него же. Отдельной копии не возникает.

const nums = [3, 1, 2];
const sorted = nums.sort((a, b) => a - b);
console.log(nums, sorted, nums === sorted);
[ 1, 2, 3 ] [ 1, 2, 3 ] true

Исходный nums уже отсортирован, а sorted — та же самая ссылка (nums === sorted дает true). Если исходный порядок нужно сохранить, это баг.

Начиная со стандарта ES2023 (Node.js 20+ и современные браузеры) есть метод toSorted — он возвращает новый отсортированный массив, не трогая исходный:

const orig = [3, 1, 2];
const copy = orig.toSorted((a, b) => a - b);
console.log(orig, copy, orig === copy);
[ 3, 1, 2 ] [ 1, 2, 3 ] false

orig остался прежним, copy — новый массив (orig === copy дает false). Если поддержка toSorted в целевой среде под вопросом, тот же эффект дает копия перед сортировкой: [...orig].sort((a, b) => a - b).

Сортировка массива объектов по полю

На практике чаще сортируют не голые числа, а объекты по одному из полей. Компаратор просто обращается к нужному свойству:

const users = [
  { name: "Anna", age: 30 },
  { name: "Boris", age: 25 },
  { name: "Vera", age: 41 },
];
const byAge = users.toSorted((a, b) => a.age - b.age);
console.log(byAge.map((u) => `${u.name}:${u.age}`).join(", "));
Boris:25, Anna:30, Vera:41

Для сортировки по строковому полю (например, по имени) вместо вычитания используют localeCompare — он учитывает алфавит конкретного языка:

const names = ["Ваня", "Аня", "Боря"];
console.log(names.sort((a, b) => a.localeCompare(b, "ru")));
[ 'Аня', 'Боря', 'Ваня' ]

Для убывания по строке результат localeCompare умножают на -1: (a, b) => b.localeCompare(a, "ru").

Сортировка в Excel: формула СОРТ и кнопки

В Excel сортировка чисел по возрастанию и убыванию делается тремя способами, и логика та же, что в JavaScript, — меняется только направление.

Кнопки. Выделите диапазон, вкладка «Главная» — «Редактирование» — «Сортировка и фильтр». Пункт «По возрастанию» (А-Я) выстраивает от меньшего к большему, «По убыванию» (Я-А) — наоборот. Для чисел, в отличие от JavaScript, отдельный компаратор не нужен: Excel сам определяет тип данных и сортирует числа как числа.

Настраиваемая сортировка. Тот же путь, пункт «Настраиваемая сортировка»: можно задать несколько уровней (столбцов) кнопкой «Добавить уровень» — сначала по одному полю, при равенстве по второму. В колонке «Порядок» выбирается «По возрастанию» или «По убыванию».

Формула СОРТ (SORT). В версиях с динамическими массивами (Microsoft 365 и Excel 2021+) есть формула, которая сортирует диапазон без изменения исходных данных и пересчитывается автоматически:

  • =СОРТ(A1:A10) — по возрастанию (третий аргумент по умолчанию 1);
  • =СОРТ(A1:A10;1;-1) — по убыванию (третий аргумент -1);
  • =СОРТПО(A1:A10;B1:B10;-1) — сортирует диапазон A по ключу из диапазона B по убыванию.

Старые функции =НАИМЕНЬШИЙ(диапазон;k) и =НАИБОЛЬШИЙ(диапазон;k) возвращают k-е по величине значение и позволяли имитировать сортировку до появления СОРТ; в актуальных версиях удобнее СОРТ.

JavaScript и Excel: чем отличается сортировка

Свойство JavaScript Excel
Числа по возрастанию arr.sort((a, b) => a - b) кнопка А-Я или =СОРТ(диапазон)
Числа по убыванию arr.sort((a, b) => b - a) кнопка Я-А или =СОРТ(диапазон;1;-1)
Числа без явного порядка сортируются как строки (нужен компаратор) сортируются как числа автоматически
Меняет исходные данные sort — да, toSorted — нет кнопки — да, формула СОРТ — нет
Сортировка по нескольким ключам несколько сравнений в компараторе «Добавить уровень» или СОРТПО
Строки/алфавит localeCompare(a, b, "ru") по правилам локали автоматически

Главное отличие для новичка: в Excel числа по умолчанию сортируются как числа, а в JavaScript sort без компаратора трактует их как строки. Отсюда и разница в результате [1, 2, 10].

Выводы

  • В JavaScript для чисел компаратор в sort обязателен: без него [1, 2, 10].sort() дает [1, 10, 2], потому что элементы сравниваются как строки.
  • Возрастание — (a, b) => a - b, убывание — (a, b) => b - a; знак разности и есть ответ компаратора.
  • sort мутирует исходный массив и возвращает ссылку на него; toSorted (ES2023, Node.js 20+) возвращает новый массив, не трогая исходный.
  • Объекты сортируют по полю (a.age - b.age), строки — через localeCompare, а не вычитанием.
  • В Excel числа сортируются как числа сразу: кнопки А-Я / Я-А, «Настраиваемая сортировка» для нескольких уровней и формула СОРТ (СОРТ;1;-1 для убывания), которая не меняет исходные данные.

Где применяется / связь с практикой

Сортировка — базовая операция в почти любом коде на JavaScript: вывод товаров по цене, задач по дате, пользователей по имени, рейтингов по убыванию. Понимание разницы между sort и toSorted и привычка всегда писать компаратор для чисел избавляют от целого класса плавающих багов, когда список «иногда» упорядочен неверно.

Освойте тему на практике

Разобраться с массивами, методами sort/toSorted, коллбэками и остальной базой языка системно помогает курс JavaScript Basic: там сортировка разбирается на реальных задачах, а не на изолированных примерах. Посмотреть формат занятий и темы можно на открытых уроках Otus — вход бесплатный.

FAQ

Почему [1, 2, 10].sort() возвращает [1, 10, 2]?
Потому что sort без аргументов приводит элементы к строкам и сравнивает посимвольно: символ 1 меньше символа 2, поэтому "10" встает раньше "2". Для чисел нужен компаратор (a, b) => a - b.

Чем toSorted отличается от sort и когда его брать?
sort меняет исходный массив на месте, toSorted возвращает новую отсортированную копию и оставляет исходный нетронутым. Берите toSorted, когда исходный порядок нужно сохранить (например, массив пришел из стороннего кода или состояния приложения).

Как в Excel отсортировать по одному столбцу, а при равенстве — по второму?
Через «Настраиваемая сортировка»: добавьте первый уровень по основному столбцу, затем «Добавить уровень» и укажите второй столбец. Или формулой СОРТПО с несколькими массивами-ключами.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)