Learn JavaScript

sort в JavaScript: как сортировать числа правильно

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

  • по умолчанию sort() сортирует элементы как строки
  • sort() изменяет исходный массив

Чтобы правильно сортировать числа, почти всегда нужен compare function.

sort по умолчанию сортирует как строки

const nums = [1, 2, 10, 21];

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

Так произошло потому, что значения сравниваются как строки: “10” идет перед “2”.

Правильная сортировка чисел: compare function

sort может принимать функцию сравнения:

arr.sort((a, b) => a - b);

По возрастанию

const nums = [1, 2, 10, 21];

nums.sort((a, b) => a - b);
console.log(nums); // [1, 2, 10, 21]

По убыванию

const nums = [1, 2, 10, 21];

nums.sort((a, b) => b - a);
console.log(nums); // [21, 10, 2, 1]

Как работает compare function

Функция сравнения получает два элемента a и b и должна вернуть число:

  • < 0 - a идет раньше b
  • > 0 - a идет позже b
  • 0 - порядок между a и b не меняется (или не важен)

Для чисел удобно использовать a - b и b - a.

sort изменяет массив: как сортировать без изменения исходного

sort мутирует массив. Если исходный массив нужен дальше без изменений, сначала делается копия.

Через slice():

const nums = [3, 1, 10, 2];

const sorted = nums.slice().sort((a, b) => a - b);

console.log(sorted); // [1, 2, 3, 10]
console.log(nums);   // [3, 1, 10, 2]

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

const users = [
  { id: 3, name: "Kim" },
  { id: 1, name: "Sam" },
  { id: 2, name: "Alex" }
];

const byId = users.slice().sort((a, b) => a.id - b.id);
console.log(byId);
// [{ id: 1, name: "Sam" }, { id: 2, name: "Alex" }, { id: 3, name: "Kim" }]

Сортировка строк: localeCompare

Для строк часто используют localeCompare, чтобы сравнение было корректнее для разных языков.

const names = ["Zoe", "Ann", "Eva"];

const sorted = names.slice().sort((a, b) => a.localeCompare(b));
console.log(sorted);

Осторожно с “грязными” данными

Если в массиве встречаются строки и числа, сравнение может дать неожиданные результаты. Лучше приводить данные к одному типу заранее.

const values = ["10", 2, "3", 1];

const nums = values.map(v => Number(v));
const sorted = nums.sort((a, b) => a - b);

console.log(sorted); // [1, 2, 3, 10]

Если Number(v) может дать NaN, стоит проверять это отдельно (см. шаг про NaN).

Частые ошибки

1) sort() для чисел без compare function

const nums = [2, 11, 3];
nums.sort();
console.log(nums); // [11, 2, 3]

2) Неожиданное изменение исходного массива

const nums = [3, 1, 2];

const sorted = nums.sort((a, b) => a - b);

console.log(sorted); // [1, 2, 3]
console.log(nums);   // [1, 2, 3]

Если нужен исходный порядок, нужна копия:

const nums = [3, 1, 2];

const sorted = nums.slice().sort((a, b) => a - b);

console.log(sorted); // [1, 2, 3]
console.log(nums);   // [3, 1, 2]

Что стоит запомнить

  • sort() по умолчанию сортирует как строки.
  • Для чисел нужен compare function: (a, b) => a - b.
  • sort() меняет исходный массив, для безопасной сортировки нужна копия.
  • Для объектов сортируют по полю: (a, b) => a.field - b.field.