Обычное лексикографическое сравнение строк в JavaScript основано на кодовых точках Unicode и не учитывает семантику чисел. Это приводит к неожиданным результатам при сортировке значений, содержащих числа:
["file1", "file2", "file10", "file20"].sort()
// ["file1", "file10", "file2", "file20"]
Причина в том, что сравнение выполняется посимвольно: символ
"1" в "file10" оказывается меньше
"2" в "file2", хотя с точки зрения человека
порядок должен быть другим.
Для решения этой проблемы используется механизм
локализованного сравнения строк с числовой
чувствительностью, реализованный через
Intl.Collator.
Класс Intl.Collator предоставляет средство для
языково-зависимого сравнения строк. Он учитывает правила конкретной
локали и дополнительные параметры сортировки.
Ключевая опция для числовой сортировки:
numeric: true — включает «естественное» сравнение чисел
внутри строкПример:
const collator = new Intl.Collator("en", { numeric: true });
["file1", "file2", "file10", "file20"].sort(collator.compare);
// ["file1", "file2", "file10", "file20"]
В этом режиме последовательности цифр внутри строки интерпретируются как целые числа, а не как набор символов.
При включённой опции numeric алгоритм сравнения
разбивает строку на сегменты:
Далее применяется гибридная логика:
Таким образом:
"file2" < "file10"
потому что 2 < 10, а не потому что символ
'2' меньше '1'.
Аналогичный результат можно получить через
String.prototype.localeCompare, который принимает те же
параметры:
["file1", "file2", "file10"].sort((a, b) =>
a.localeCompare(b, "en", { numeric: true })
);
Разница:
Intl.Collator создаётся один раз и
переиспользуетсяlocaleCompare создаёт временный механизм при каждом
вызовеДля больших массивов предпочтителен Intl.Collator:
const collator = new Intl.Collator("en", { numeric: true });
array.sort(collator.compare);
Числовая интерпретация работает независимо от языка, но порядок строковой части зависит от локали:
const ruCollator = new Intl.Collator("ru", { numeric: true });
["a2", "a10", "a1"].sort(ruCollator.compare);
// ["a1", "a2", "a10"]
При этом правила алфавита определяются языком, а числа обрабатываются универсально.
Частый сценарий — сортировка версий:
["v1.2.0", "v1.10.0", "v1.3.0"].sort(collator.compare);
Результат:
v1.2.0
v1.3.0
v1.10.0
Каждая числовая группа сравнивается независимо.
Важно учитывать, что:
. - _) не
объединяют числа["file01", "file1", "file002"].sort(collator.compare);
Результат зависит от реализации, но обычно:
file1
file01
file002
Числа сравниваются как значения, но строковая структура может влиять на вторичную сортировку.
["1.2", "1.10", "1.3"].sort(collator.compare);
Результат:
1.2
1.3
1.10
Точка разделяет числовые сегменты, поэтому сравнение идёт по частям
(1, 2 vs 1, 10).
["img20version3", "img3version12", "img20version1"].sort(collator.compare);
Числовая логика применяется к каждому числовому сегменту независимо, что даёт естественный порядок для человеческого восприятия.
Intl.Collator не трактует знак минус как часть числа.
Строки:
["-1", "-10", "2"]
рассматриваются как обычный текст с символами, а не как числовые значения.
Для полноценной числовой сортировки отрицательных чисел требуется предварительное преобразование:
arr.sort((a, b) => Number(a) - Number(b));
или комбинированная логика при смешанных данных.
Определяет строгость сравнения:
"base" — игнорирует диакритику и регистр"accent" — учитывает диакритику"case" — учитывает регистр"variant" — максимально строгий режимПри числовой сортировке обычно используется:
new Intl.Collator("en", { numeric: true, sensitivity: "base" });
Определяет приоритет регистра:
"upper""lower""false"В числовой сортировке влияет только на текстовые сегменты, не затрагивая числа.
Игнорирует знаки пунктуации при сравнении текста:
new Intl.Collator("en", {
numeric: true,
ignorePunctuation: true
});
Полезно для строк вроде:
"file-2", "file2"
Создание Intl.Collator — относительно дорогая операция.
При сортировке больших массивов важно избегать повторного создания
объекта:
// плохо
array.sort((a, b) =>
new Intl.Collator("en", { numeric: true }).compare(a, b)
);
// хорошо
const collator = new Intl.Collator("en", { numeric: true });
array.sort(collator.compare);
Дополнительно:
Collator можно использовать многократноСортировка через Intl.Collator в современных движках
обычно стабильна, но спецификация JavaScript не гарантирует стабильность
Array.prototype.sort во всех реализациях.
При необходимости строгой стабильности применяется декорирование:
array
.map((value, index) => ({ value, index }))
.sort((a, b) => {
const res = collator.compare(a.value, b.value);
return res !== 0 ? res : a.index - b.index;
})
.map(item => item.value);
["doc1", "doc20", "doc3"]
["2.0.1", "2.0.10", "2.0.2"]
["item9", "item10", "item11"]
["user2session3", "user10session1"]
Числовой режим Intl.Collator не является полноценным
числовым парсером. Он не решает задачи:
1e10)Он работает исключительно в рамках строковой интерпретации числовых сегментов.
Хотя числовая логика универсальна, локаль влияет на текстовые части:
const de = new Intl.Collator("de", { numeric: true });
const en = new Intl.Collator("en", { numeric: true });
Различия проявляются при сортировке букв, но не чисел внутри строк.