LocaleCompare в JavaScript: правильная и удобная сортировка строк с учетом языка
- среда, 12 февраля 2025 г. в 00:00:03
Привет, меня зовут Дмитрий, я React-разработчик, и сегодня хочу рассказать о методе localeCompare в JavaScript. Мне кажется, что этот метод не так часто используется при сортировке строк, хотя он действительно заслуживает внимания. Многие привыкли к стандартным методам сравнения, но localeCompare позволяет учесть важные нюансы, такие как языковые особенности, регистр символов и числовую сортировку. Я постараюсь раскрыть все его возможности и показать, как можно использовать его для улучшения сортировки данных в проектах.
При работе со строками в JavaScript часто возникает необходимость их сравнивать и сортировать. Например, это требуется при отображении списка имен пользователей, упорядочивании товаров в интернет-магазине или обработке текстовых данных. На первый взгляд, задача может показаться простой — достаточно использовать операторы сравнения (>, <, ===). Однако такой подход имеет ряд недостатков, особенно если учитывать языковые особенности, регистр букв и специальные символы.
В JavaScript строки сравниваются посимвольно на основе их кодов Unicode. Это может приводить к неожиданным результатам:
console.log("apple" > "banana"); // false (ожидаемо)
console.log("Zebra" > "apple"); // false (ожидали true, но код 'Z' < 'a')
console.log("straße" === "Strasse"); // false (в немецком языке они считаются одинаковыми)
Основные проблемы:
Регистрозависимость. Например, "Zebra" > "apple" возвращает false, так как заглавная Z имеет меньший Unicode-код, чем строчная a.
Проблемы с диакритическими знаками. "straße" !== "Strasse", хотя в немецком языке эти слова считаются идентичными.
Некорректное сравнение строк с числами. Например:
console.log(["file10", "file2"].sort()); // ["file10", "file2"]
Ожидаемый порядок: ["file2", "file10"], но строки сравниваются посимвольно, и "1" в "file10" идет раньше "2" в "file2".
Метод localeCompare позволяет сравнивать строки с учетом языковых особенностей, правил сортировки и дополнительных параметров. Например:
console.log('ёжик'.localeCompare('яблоко', 'ru')); // -1 (Ё считается перед Я)
console.log('ёлка'.localeCompare('ежевика', 'ru', { sensitivity: 'base' })); // -1 (игнорируется разница между Ё и Е)
console.log('файл10'.localeCompare('файл2', 'ru', { numeric: true })); // 1 (правильная числовая сортировка)
console.log('мост'.localeCompare('Москва', 'ru', { sensitivity: 'case' })); // 1 (различается регистр)
В русском языке буква Ё идет перед Я, поэтому "ёжик" раньше "яблоко".
При sensitivity: "base" буквы Е и Ё считаются одинаковыми.
numeric: true позволяет правильно сортировать строки с числами (обычно "файл10" шло бы перед "файл2").
sensitivity: "case" различает регистр, "Москва" будет раньше "мост".
Этот метод полезен для работы с кириллическими текстами, особенно в случае алфавитных списков и каталогов.
Корректное сравнение строк в разных языках. Учитываются особенности алфавитов и диакритические знаки.
Настраиваемая сортировка. Можно игнорировать регистр, учитывать числовые значения и изменять порядок сортировки.
Поддержка во всех современных браузерах. Это встроенный метод, который не требует дополнительных библиотек.
Метод localeCompare используется для сравнения двух строк с учетом языковой локали и дополнительных параметров. В отличие от стандартного посимвольного сравнения, он учитывает особенности алфавита, регистр, числовые значения и национальные правила сортировки.
string1.localeCompare(string2, [locales], [options])
Описание параметров:
string2 - строка, с которой производится сравнение.
locales - (необязательный) — код языка или массив кодов, например "en" (английский), "ru" (русский), "de" (немецкий). Если не указано, используется локаль системы.
options - (необязательный) — объект с настройками, влияющими на поведение сравнения.
Пример без указания локали:
console.log("apple".localeCompare("banana")); // -1 (apple перед banana)
console.log("banana".localeCompare("apple")); // 1 (banana после apple)
console.log("apple".localeCompare("apple")); // 0 (строки равны)
Пример с указанием локали:
console.log("ä".localeCompare("z", "de")); // -1 (в немецком "ä" идет перед "z")
console.log("ä".localeCompare("z", "sv")); // 1 (в шведском "ä" идет после "z")
Метод localeCompare поддерживает объект options, который позволяет настраивать поведение сравнения строк. С его помощью можно учитывать регистр, диакритические знаки, числовые значения и игнорировать знаки препинания.
Основные параметры options:
sensitivity - определяет, учитываются ли регистр и диакритические знаки.
caseFirst - задает, какие буквы (заглавные или строчные) должны идти первыми при сортировке.
numeric - включает естественную числовую сортировку.
ignorePunctuation - позволяет игнорировать знаки препинания при сравнении.
Учет регистра (caseFirst):
Этот параметр определяет, должны ли заглавные буквы идти перед строчными или наоборот.
console.log("a".localeCompare("B", "en", { caseFirst: "upper" })); // 1 ("B" идет перед "a")
console.log("a".localeCompare("B", "en", { caseFirst: "lower" })); // -1 ("a" идет перед "B")
Доступные значения:
"upper" - заглавные буквы идут первыми.
"lower" - строчные буквы идут первыми.
"false" - порядок определяется локалью.
Позволяет игнорировать или учитывать регистр и акцентные знаки, такие как «е» и «ё»
console.log("россия".localeCompare("Россия", "ru", { sensitivity: "case" })); // -1 (различает регистр)
console.log("жёлтый".localeCompare("желтый", "ru", { sensitivity: "base" })); // 0 (игнорирует диакритику)
console.log("жёлтый".localeCompare("желтый", "ru", { sensitivity: "accent" })); // 1 (различает акценты)
В первом случае различается регистр (русский символ "р" и "Р"). Во втором случае игнорируются акценты (например, "ё" и "е"). В третьем случае различаются акценты.
Доступные значения:
"base" - игнорирует регистр и диакритические знаки.
"accent" - различает акцентные знаки, но игнорирует регистр.
"case" - различает регистр, но игнорирует диакритические знаки.
"variant" - учитывает и регистр, и диакритические знаки.
Полезно при сортировке строк с числами, например, имен файлов.
console.log("file10".localeCompare("file2", "en", { numeric: true })); // 1 (file2 идет перед file10)
console.log("file10".localeCompare("file2", "en", { numeric: false })); // -1 (обычное посимвольное сравнение)
По умолчанию строки сравниваются посимвольно ("1" идет перед "2"), но с numeric: true числа распознаются и сравниваются как целые.
Позволяет не учитывать знаки препинания при сравнении строк.
console.log("hello!".localeCompare("hello", "en", { ignorePunctuation: true })); // 0
console.log("word, word".localeCompare("word word", "en", { ignorePunctuation: true })); // 0
Этот параметр полезен при обработке текста, если знаки препинания не должны влиять на порядок сортировки.
Метод localeCompare особенно полезен в русскоязычных проектах, так как стандартное сравнение (>, <) не учитывает особенности алфавита, регистр и числовые значения. А еще очень удобно, если нужно отсортировать числа, записанные строками. Рассмотрим несколько практических примеров.
При использовании sort() строки сортируются посимвольно по таблице Unicode, что может давать некорректный порядок в русском языке. localeCompare позволяет учесть локаль.
const words = ["яблоко", "Вишня", "груша", "Апельсин", "банан"];
words.sort((a, b) => a.localeCompare(b, "ru"));
console.log(words); // ["Апельсин", "банан", "Вишня", "груша", "яблоко"]
По умолчанию localeCompare чувствителен к регистру. Заглавные буквы сортируются отдельно от строчных. Чтобы этого избежать, можно использовать sensitivity: "base".
words.sort((a, b) => a.localeCompare(b, "ru", { sensitivity: "base" })); console.log(words); // ["Апельсин", "банан", "Вишня", "груша", "яблоко"]
Частая задача — сортировка массива объектов по названию, имени или другому строковому свойству.
const users = [
{ name: "Сергей" },
{ name: "Анна" },
{ name: "Юлия" },
{ name: "Борис" }
];
users.sort((a, b) => a.name.localeCompare(b.name, "ru")); console.log(users);
/* [ { name: "Анна" }, { name: "Борис" }, { name: "Сергей" }, { name: "Юлия" } ] */
Если нужно, чтобы заглавные и строчные буквы не влияли на сортировку, добавляем sensitivity: "base".
users.sort((a, b) => a.name.localeCompare(b.name, "ru", { sensitivity: "base" }));
Хотя localeCompare — мощный инструмент для локализованного сравнения строк, он имеет ряд ограничений, о которых важно знать при использовании в реальных проектах.
LocaleCompare работает значительно медленнее, чем стандартные операторы сравнения
(>, <), так как учитывает сложные правила локалей. При обработке больших массивов его использование может снизить производительность. Например, сортировка массива из 100 000 строк с помощью localeCompare будет ощутимо медленнее, чем при использовании стандартного Unicode-сравнения.
Альтернативы:
Если важна скорость, но не требуется строгая локализованная сортировка, можно привести строки к нижнему регистру и использовать localeCompare с sensitivity: "base", что немного ускорит процесс.
Для англоязычных данных и технических строк можно использовать обычные операторы сравнения.
В случае частого использования стоит кешировать результаты сравнения.
Хотя современные браузеры хорошо поддерживают localeCompare, в устаревших версиях могут возникать проблемы.
Вот так можно проверить доступные локали
console.log(Intl.Collator.supportedLocalesOf(["ru", "en", "fr"]));
Если браузер не поддерживает указанную локаль, результат сортировки может отличаться от ожидаемого.
Альтернатива:
Использовать Intl.Collator, который иногда работает быстрее и предоставляет больше возможностей.
const collator = new Intl.Collator("ru", { sensitivity: "base" });
arr.sort((a, b) => collator.compare(a, b));
localeCompare — удобный метод для локализованного сравнения строк, но в некоторых случаях его использование может быть неэффективным. Для ускорения сортировки или поиска на больших данных стоит рассмотреть Intl.Collator, предварительную нормализацию данных или специализированные библиотеки.