Сортировка массивов объектов в JavaScript часто упирается не в
алгоритм sort, а в правила сравнения строковых значений.
Обычное лексикографическое сравнение Unicode-кодов приводит к
неожиданным результатам: регистр влияет на порядок, диакритика ломает
ожидаемую последовательность, числовые строки сортируются
некорректно.
const users = [
{ name: "zebra" },
{ name: "Änne" },
{ name: "anna" },
{ name: "Álvaro" }
];
users.sort((a, b) => a.name.localeCompare(b.name));
Метод localeCompare уже учитывает локаль, но его прямое
использование в каждом сравнении может быть избыточным по
производительности и недостаточно гибким для сложных сценариев
сортировки.
Объект Intl.Collator предоставляет специализированный
механизм сравнения строк с учётом языковых правил. Он позволяет заранее
создать функцию сравнения, оптимизированную под конкретную локаль и
параметры.
const collator = new Intl.Collator("ru", { sensitivity: "base" });
const words = ["ёж", "елка", "яблоко", "Ель"];
words.sort(collator.compare);
Intl.Collatorlocale — определяет правила языка сортировки options — управляет поведением сравнения:
sensitivity
"base" — игнорирует регистр и диакритику"accent" — учитывает диакритику, игнорирует
регистр"case" — учитывает регистр, игнорирует диакритику"variant" — строгая сортировкаnumeric
true — корректная сортировка чисел внутри строкconst collator = new Intl.Collator("en", { numeric: true });
["item2", "item10", "item1"].sort(collator.compare);
// item1, item2, item10
Наиболее частый сценарий — сортировка объектов по имени, названию, заголовку.
const products = [
{ title: "iPhone 12" },
{ title: "iPhone 5" },
{ title: "iPhone 11" }
];
const collator = new Intl.Collator("en", { numeric: true });
products.sort((a, b) => collator.compare(a.title, b.title));
Использование Collator предпочтительнее
localeCompare в цикле сортировки, так как сравнение уже
оптимизировано и не создаёт повторных вычислений локали.
Реальные данные редко сортируются по одному полю. Обычно применяется цепочка критериев: сначала фамилия, затем имя, затем дата.
const users = [
{ firstName: "Ivan", lastName: "Petrov" },
{ firstName: "Anna", lastName: "Petrova" },
{ firstName: "Ivan", lastName: "Ivanov" }
];
const collator = new Intl.Collator("ru");
users.sort((a, b) => {
const last = collator.compare(a.lastName, b.lastName);
if (last !== 0) return last;
return collator.compare(a.firstName, b.firstName);
});
Такая схема обеспечивает детерминированную сортировку без зависимости от порядка исходных данных.
Современные реализации JavaScript гарантируют стабильность
Array.prototype.sort, однако поведение зависит от движка.
При использовании Intl.Collator важно учитывать, что:
Collator внутри sort ухудшает
производительностьCollator должен использоваться
повторно// плохая практика
array.sort((a, b) =>
new Intl.Collator("en").compare(a.name, b.name)
);
// корректная практика
const collator = new Intl.Collator("en");
array.sort((a, b) => collator.compare(a.name, b.name));
В данных часто встречаются строки с числами: версии, артикулы, идентификаторы.
Без numeric: true сортировка ломается:
["v1", "v10", "v2"].sort();
// v1, v10, v2
С Intl.Collator:
const collator = new Intl.Collator("en", { numeric: true });
["v1", "v10", "v2"].sort(collator.compare);
// v1, v2, v10
Это особенно важно для версионирования и списков файлов.
Параметр sensitivity влияет на то, как строки
различаются:
const collator = new Intl.Collator("en", { sensitivity: "base" });
["a", "A", "á", "b"].sort(collator.compare);
"base" — группирует визуально одинаковые символы"accent" — различает акценты"case" — различает регистр"variant" — учитывает всёВыбор зависит от доменной логики. Для UI-списков часто используется
"base", для юридических или точных данных —
"variant".
Когда несколько полей требуют локализованного сравнения, применяется комбинирование:
const collator = new Intl.Collator("ru");
const data = [
{ city: "Москва", street: "Арбат" },
{ city: "Москва", street: "Бауманская" },
{ city: "Астана", street: "Абая" }
];
data.sort((a, b) => {
const cityCompare = collator.compare(a.city, b.city);
if (cityCompare !== 0) return cityCompare;
return collator.compare(a.street, b.street);
});
При сортировке больших массивов критично минимизировать количество сравнений и тяжёлых операций:
Intl.Collator выполняется один разcompare используется как чистая функцияОптимизированный вариант:
const collator = new Intl.Collator("ru", {
numeric: true,
sensitivity: "base"
});
const sorted = items.sort((a, b) =>
collator.compare(a.label, b.label)
);
В прикладных сценариях сортировка часто зависит от пользовательских настроек: язык интерфейса, режим отображения, тип сортировки.
function sortUsers(users, locale) {
const collator = new Intl.Collator(locale, {
numeric: true,
sensitivity: "base"
});
return users.sort((a, b) => collator.compare(a.name, b.name));
}
При смене языка интерфейса создаётся новый Collator,
соответствующий выбранной локали.
Сортировка часто комбинируется с другими объектами
Intl:
Intl.DateTimeFormat — сортировка по форматированным
датамIntl.NumberFormat — отображение чисел при
сортировкеIntl.PluralRules — управление множественными формами
при выводе списковПример сортировки по датам:
const formatter = new Intl.DateTimeFormat("ru");
events.sort((a, b) =>
new Date(a.date) - new Date(b.date)
);
Хотя здесь Intl.Collator не применяется напрямую,
принцип локализации сохраняется: данные сортируются в соответствии с
культурным контекстом отображения.
В реальных структурах данные часто неоднородны:
const items = [
{ label: "file2" },
{ label: "file10" },
{ label: "file1" }
];
Правильная сортировка:
const collator = new Intl.Collator("en", { numeric: true });
items.sort((a, b) => collator.compare(a.label, b.label));
Это устраняет необходимость писать собственные парсеры чисел внутри строк.
При использовании Intl.Collator важно учитывать:
Несмотря на это, Intl.Collator остаётся стандартным и
наиболее надёжным инструментом для локализованного сравнения строк в
JavaScript.
Универсальный шаблон сортировки объектов с локализацией:
const collator = new Intl.Collator(locale, options);
function sortByField(array, field) {
return array.sort((a, b) =>
collator.compare(a[field], b[field])
);
}
Расширение для нескольких полей:
function multiSort(a, b) {
let res = collator.compare(a.lastName, b.lastName);
if (res !== 0) return res;
res = collator.compare(a.firstName, b.firstName);
if (res !== 0) return res;
return collator.compare(a.middleName, b.middleName);
}
Такая структура масштабируется на сложные модели данных без потери читаемости и предсказуемости результата.