Intl API (ECMAScript Internationalization API) обеспечивает стандартизированный набор инструментов для локализации в JavaScript, включая сравнение строк, форматирование дат и чисел, а также работу с локалью пользователя. Одной из менее очевидных, но практически значимых областей применения становится реализация бинарного поиска в локализованных данных, где порядок элементов определяется не ASCII-сортировкой, а правилами конкретного языка.
Классический бинарный поиск предполагает строго определённое условие: массив отсортирован по одному и тому же компаратору, который используется во время поиска. В случае локализации это условие усложняется, поскольку порядок строк зависит от языка, регистра, диакритики и культурных правил сравнения.
Обычное лексикографическое сравнение:
Поэтому ключевым элементом становится Intl.Collator,
который формирует корректную функцию сравнения:
locale)sensitivity)numeric)Бинарный поиск в локализованной среде возможен только при строгом соблюдении правила: массив должен быть отсортирован тем же компаратором, который используется при поиске.
Типичный этап подготовки данных:
const collator = new Intl.Collator("ru", {
sensitivity: "base"
});
const data = ["яблоко", "ёж", "арбуз", "груша"];
data.sort((a, b) => collator.compare(a, b));
Здесь важно, что сортировка выполняется не стандартным
localeCompare, а через Intl.Collator,
поскольку он позволяет:
Бинарный поиск в локализованном контексте не отличается по структуре, но полностью зависит от функции сравнения:
function binarySearch(arr, target, collator) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const comparison = collator.compare(arr[mid], target);
if (comparison === 0) {
return mid;
}
if (comparison < 0) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
Ключевой момент заключается в том, что collator.compare
заменяет все предположения о порядке строк. В отличие от обычного
< или >, он возвращает значения,
соответствующие правилам локали.
Параметр sensitivity в Intl.Collator
определяет, какие различия считаются значимыми:
base — игнорируются диакритика и регистрaccent — учитываются диакритические знакиcase — учитывается регистрvariant — полное различие символовЭти настройки напрямую влияют на бинарный поиск, поскольку меняют поведение сравнения.
Пример:
const collator = new Intl.Collator("fr", {
sensitivity: "base"
});
В таком режиме строки "e", "é",
"E" считаются равными с точки зрения поиска, что приводит к
необходимости учитывать возможность нескольких совпадений и расширения
результата за пределы первого найденного индекса.
Опция numeric: true изменяет поведение сравнения так,
что числа внутри строк сортируются по числовому значению:
const collator = new Intl.Collator("en", {
numeric: true
});
Это критично для данных вида:
Без numeric: true порядок будет лексикографическим, что
ломает бинарный поиск в пользовательских интерфейсах файловых списков,
каталогов и логов.
Для повышения производительности часто используется стратегия предвычисления сортировочных ключей:
const collator = new Intl.Collator("ru");
const decorated = data.map(item => ({
value: item,
key: collator.compare.bind(collator, item)
}));
Однако такой подход ограничен: бинарный поиск требует строгого порядка, а не динамической функции сравнения. Поэтому более корректный метод — хранить массив уже отсортированных значений, а не функций.
Локализация часто связана с Unicode-нормализацией. Строки могут выглядеть одинаково, но иметь разное внутреннее представление:
Перед сортировкой и поиском данные должны быть приведены к единой форме:
function normalize(str) {
return str.normalize("NFC");
}
Использование нормализации до передачи в Intl.Collator
предотвращает ситуации, когда бинарный поиск не находит существующий
элемент из-за различий в представлении строки.
Разные локали могут давать различный порядок одного и того же набора данных. Например:
Это означает, что бинарный поиск должен быть привязан к конкретной локали на уровне всей структуры данных. Изменение локали требует полной пересортировки массива.
Создание Intl.Collator — операция сравнительно дорогая.
В высоконагруженных сценариях применяется кэширование:
const collators = new Map();
function getCollator(locale) {
if (!collators.has(locale)) {
collators.set(locale, new Intl.Collator(locale, { sensitivity: "base" }));
}
return collators.get(locale);
}
Это особенно важно при множественных бинарных поисках в больших массивах, например:
Несмотря на корректную реализацию через Intl.Collator,
бинарный поиск имеет ограничения:
В локализованных системах часто применяется гибридный подход:
Метод String.prototype.localeCompare является обёрткой
над Intl.Collator, но:
Использование Intl.Collator позволяет отделить создание
стратегии сравнения от её применения, что критично для бинарного
поиска.
При реализации бинарного поиска в локализованных данных часто возникают следующие проблемы:
Любое нарушение этих условий делает бинарный поиск некорректным, даже если алгоритм формально реализован правильно.
Локализованный бинарный поиск используется в:
Во всех этих системах ключевым фактором является не скорость самого
поиска, а корректность культурного порядка, задаваемого
Intl.Collator.