Сортировка строк в JavaScript по умолчанию опирается на Unicode-коды символов, что приводит к результатам, не соответствующим языковым ожиданиям. Например, строки с заглавными буквами могут идти отдельно от строчных, а символы с диакритикой обрабатываются как «более поздние» или «менее ранние» в зависимости от их кодовой позиции, а не лингвистического смысла.
Для управления этими особенностями используется
Intl.Collator — часть Internationalization API, реализующая
алгоритмы сравнения строк на основе правил конкретных языков и
культурных норм.
Intl.Collator предоставляет метод сравнения, который
можно передавать в Array.prototype.sort.
const collator = new Intl.Collator('ru');
const words = ['яблоко', 'Яндекс', 'арбуз', 'ёж', 'елка'];
words.sort(collator.compare);
console.log(words);
Метод compare возвращает:
Это поведение соответствует контракту функции сравнения для
sort.
Первый аргумент Intl.Collator — локаль, определяющая
правила сортировки:
const collator = new Intl.Collator('de');
Локаль влияет на:
При отсутствии локали используется локаль окружения выполнения.
Одной из ключевых задач кастомной сортировки является управление «строгостью» сравнения.
const collator = new Intl.Collator('en', {
sensitivity: 'base'
});
Значение sensitivity:
base — игнорирует регистр и диакритикуaccent — учитывает диакритику, но не регистрcase — учитывает регистр, но не диакритикуvariant — учитывает всёПример:
const collator = new Intl.Collator('en', { sensitivity: 'base' });
['a', 'A', 'á', 'b'].sort(collator.compare);
// ['a', 'A', 'á', 'b']
Параметр caseFirst задаёт приоритет заглавных или
строчных букв:
const collator = new Intl.Collator('en', {
sensitivity: 'variant',
caseFirst: 'upper'
});
Возможные значения:
upper — заглавные первымиlower — строчные первымиfalse — без приоритета['a', 'A', 'b', 'B'].sort(collator.compare);
Результат зависит от выбранной стратегии.
Обычная сортировка строк приводит к неожиданным результатам:
['1', '2', '10', '20'].sort();
// ['1', '10', '2', '20']
Включение числового режима решает проблему:
const collator = new Intl.Collator('en', {
numeric: true
});
['1', '2', '10', '20'].sort(collator.compare);
// ['1', '2', '10', '20']
Этот режим анализирует числовые последовательности внутри строки и сравнивает их как числа.
Параметр ignorePunctuation позволяет игнорировать знаки
препинания при сравнении:
const collator = new Intl.Collator('en', {
ignorePunctuation: true
});
Пример:
['a-b', 'ab', 'a b'].sort(collator.compare);
Без игнорирования пунктуации эти строки могут иметь неожиданный
порядок из-за символов - и пробелов.
Хотя Intl.Collator определяет правила сравнения,
стабильность сортировки зависит от реализации движка. В современных
движках сортировка обычно стабильна, но это не гарантируется
стандартом.
При необходимости строгой стабильности часто используется комбинация индекса и сравнения:
const collator = new Intl.Collator('en');
const data = ['b', 'a', 'a', 'c'];
const sorted = data
.map((value, index) => ({ value, index }))
.sort((a, b) =>
collator.compare(a.value, b.value) || a.index - b.index
)
.map(item => item.value);
Кастомная сортировка часто требует комбинации нескольких факторов:
const collator = new Intl.Collator('ru', {
sensitivity: 'base',
numeric: true
});
function compareItems(a, b) {
return (
collator.compare(a.name, b.name) ||
a.priority - b.priority ||
a.id - b.id
);
}
Такой подход позволяет строить многоуровневую систему сортировки.
Intl.Collator.compare может использоваться не только в
sort, но и в других структурах сравнения:
const collator = new Intl.Collator('en');
const set = new Set(['z', 'a', 'm']);
[...set].sort(collator.compare);
Также он применяется при поиске позиции вставки:
function findInsertIndex(arr, value, collator) {
let low = 0;
let high = arr.length;
while (low < high) {
const mid = (low + high) >> 1;
if (collator.compare(arr[mid], value) < 0) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
Создание экземпляра Intl.Collator — относительно дорогая
операция. В высоконагруженных сценариях объект создаётся один раз и
переиспользуется:
const collator = new Intl.Collator('en', { numeric: true });
function sortData(data) {
return data.sort(collator.compare);
}
Избегание повторного создания особенно важно при сортировке больших массивов или частых обновлениях интерфейса.
Разные языки задают различные правила сравнения:
const tr = new Intl.Collator('tr');
['i', 'ı', 'I', 'İ'].sort(tr.compare);
Результат может существенно отличаться от английской или русской локали, что важно учитывать при интернациональных системах.
Некоторые движки поддерживают дополнительные параметры через
usage, ignorePunctuation,
numeric, но базовая модель всегда строится вокруг:
sensitivity)caseFirst)numeric)Эти параметры формируют поведение сравнения, которое ближе к естественному языковому восприятию, чем к машинному порядку Unicode-кодов.
При работе с массивами объектов Intl.Collator
используется как часть функции сравнения:
const collator = new Intl.Collator('ru', { numeric: true });
const users = [
{ name: 'Иван10' },
{ name: 'Иван2' },
{ name: 'Иван1' }
];
users.sort((a, b) => collator.compare(a.name, b.name));
Такой подход позволяет реализовать сортировку, устойчивую к смешанным числовым и строковым значениям внутри идентификаторов.
Collator сравнивает строки не только посимвольно, но и с учётом лингвистической значимости символов. При равенстве на базовом уровне применяется более глубокий анализ:
Это делает поведение более предсказуемым в многоязычных системах по сравнению с прямым лексикографическим сравнением.
String.prototype.localeCompare является обёрткой над
аналогичной логикой:
'a'.localeCompare('b', 'en');
Но Intl.Collator предпочтительнее при множественных
сравнениях, поскольку позволяет избежать повторной интерпретации
опций:
const collator = new Intl.Collator('en');
array.sort(collator.compare);
В высокопроизводительных сценариях это снижает накладные расходы и упрощает повторное использование логики сравнения.