Сортировка строк в JavaScript при работе с разными языками требует
учёта правил конкретной локали, поскольку лексикографический порядок
символов не совпадает с привычным «байтовым» или Unicode-кодовым
сравнением. Базовый механизм сортировки, использующий оператор сравнения
< и >, опирается на кодовые точки UTF-16
и не учитывает особенности языка: диакритические знаки, регистры,
составные символы и национальные алфавиты.
Стандартная сортировка массивов через
Array.prototype.sort() без компаратора использует
преобразование элементов в строки и сравнение их по Unicode-кодам.
["z", "a", "ä", "b"].sort();
Результат зависит от кодировки символов и не отражает языковую
корректность. Например, символ ä может оказаться после
z, хотя в немецкой локали он должен рассматриваться как
вариант a.
Основные ограничения такого подхода:
Эти проблемы устраняются через механизмы Intl.
Intl.Collator предоставляет объект, реализующий
сравнение строк с учётом языковых правил. Он используется как компаратор
в Array.prototype.sort().
Базовая конструкция:
const collator = new Intl.Collator("ru");
collator.compare("а", "б");
Возвращаемые значения:
Использование в сортировке:
const collator = new Intl.Collator("ru");
["яблоко", "арбуз", "груша"].sort(collator.compare);
Локаль определяет правила сравнения символов. Например:
"en" — английские правила"de" — немецкие правила (умлауты и расширенные
символы)"tr" — турецкая локаль с особой обработкой
i/İ"ru" — кириллица с собственным алфавитным порядкомПример:
const en = new Intl.Collator("en");
const de = new Intl.Collator("de");
["a", "ä", "z"].sort(en.compare);
["a", "ä", "z"].sort(de.compare);
Результаты могут различаться из-за разных трактовок
ä.
Определяет уровень различий между строками.
new Intl.Collator("en", { sensitivity: "base" });
Варианты:
"base" — игнорируются диакритика и регистр"accent" — учитываются диакритические знаки"case" — учитывается регистр"variant" — полное различие символовПример поведения:
const collator = new Intl.Collator("en", { sensitivity: "base" });
collator.compare("a", "A"); // 0
collator.compare("a", "á"); // 0
Позволяет корректно сортировать числа внутри строк.
new Intl.Collator("en", { numeric: true });
Без этого параметра сортировка будет лексикографической:
["2", "10", "1"].sort();
// ["1", "10", "2"]
С включённой числовой логикой:
const collator = new Intl.Collator("en", { numeric: true });
["2", "10", "1"].sort(collator.compare);
// ["1", "2", "10"]
Определяет приоритет регистра:
"upper" — заглавные первыми"lower" — строчные первыми"false" — без приоритетаnew Intl.Collator("en", { caseFirst: "upper" });
Метод sort() в JavaScript обычно использует адаптацию
алгоритма с временной сложностью O(n log n). Конкретная реализация
зависит от движка (V8, SpiderMonkey, JavaScriptCore), но принцип
остаётся единым: сортировка строится на основе функции сравнения.
При использовании Intl.Collator сравнение строк
делегируется ICU (International Components for Unicode), что
означает:
Intl.Collator может внутренне создавать «ключи
сортировки» (sort keys), чтобы ускорить многократные сравнения.
Без кэширования:
["яблоко", "арбуз", "груша"].sort(
(a, b) => new Intl.Collator("ru").compare(a, b)
);
Этот подход неэффективен, поскольку создаётся новый
Collator для каждого сравнения.
Оптимизированный вариант:
const collator = new Intl.Collator("ru");
["яблоко", "арбуз", "груша"].sort(collator.compare);
Строки могут иметь разные Unicode-представления одного и того же символа. Например:
ée + ́Intl.Collator учитывает нормализацию, обеспечивая
эквивалентность таких форм.
const collator = new Intl.Collator("fr");
collator.compare("é", "e\u0301");
Результат трактуется как равенство при соответствующей чувствительности.
Четыре уровня чувствительности определяют глубину сравнения:
Пример различий:
const base = new Intl.Collator("en", { sensitivity: "base" });
const variant = new Intl.Collator("en", { sensitivity: "variant" });
base.compare("resume", "résumé"); // 0
variant.compare("resume", "résumé"); // различны
Некоторые языки имеют нестандартные правила:
i, İ,
ı, Iß как
sså, ä,
ö в конецЭти правила полностью инкапсулируются в ICU и не требуют ручной обработки.
Создание Intl.Collator является относительно дорогой
операцией из-за загрузки правил локали и подготовки внутренних таблиц
сравнения. Поэтому эффективная стратегия заключается в переиспользовании
одного экземпляра:
const collator = new Intl.Collator("ru", {
sensitivity: "base",
numeric: true
});
function sortNames(list) {
return list.sort(collator.compare);
}
Такой подход снижает накладные расходы при массовых операциях сортировки.
Альтернативой является метод
String.prototype.localeCompare:
"a".localeCompare("b", "ru");
Он поддерживает параметры локали, но при массовых операциях менее
эффективен, чем предварительно созданный Intl.Collator.
Сравнение подходов:
Современные движки JavaScript обеспечивают стабильную сортировку, что означает сохранение относительного порядка равных элементов.
При использовании Intl.Collator стабильность
сохраняется, но итоговый порядок зависит от:
В сложных структурах данных часто требуется многоуровневая сортировка:
const collator = new Intl.Collator("ru", { numeric: true });
users.sort((a, b) =>
collator.compare(a.lastName, b.lastName) ||
collator.compare(a.firstName, b.firstName)
);
Такой подход реализует цепочку критериев сравнения, где следующий уровень используется только при равенстве предыдущего.
Перед использованием Intl.Collator часто выполняется
подготовка данных:
function normalize(str) {
return str.normalize("NFC");
}
Это уменьшает неоднозначности при сравнении строк из разных источников.
С точки зрения алгоритмов, процесс сортировки с локалью включает:
Таким образом, сложность операции определяется как:
Сортировка с учётом локали применяется в:
Во всех этих случаях корректность сортировки важнее чистой производительности, поскольку влияет на восприятие структуры данных пользователем.