LocaleCompare в JavaScript: правильная и удобная сортировка строк с учетом языка

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, меня зовут Дмитрий, я 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?

Метод 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:

  • Корректное сравнение строк в разных языках. Учитываются особенности алфавитов и диакритические знаки.

  • Настраиваемая сортировка. Можно игнорировать регистр, учитывать числовые значения и изменять порядок сортировки.

  • Поддержка во всех современных браузерах. Это встроенный метод, который не требует дополнительных библиотек.

Основы метода localeCompare

Метод 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")

Использование настроек (options)

Метод 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" - порядок определяется локалью.

Чувствительность к диакритическим знакам (sensitivity)

Позволяет игнорировать или учитывать регистр и акцентные знаки, такие как «е» и «ё»

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" - учитывает и регистр, и диакритические знаки.

Числовое сравнение (numeric)

Полезно при сортировке строк с числами, например, имен файлов.

console.log("file10".localeCompare("file2", "en", { numeric: true })); // 1 (file2 идет перед file10) 
console.log("file10".localeCompare("file2", "en", { numeric: false })); // -1 (обычное посимвольное сравнение)

По умолчанию строки сравниваются посимвольно ("1" идет перед "2"), но с numeric: true числа распознаются и сравниваются как целые.

Игнорирование знаков препинания (ignorePunctuation)

Позволяет не учитывать знаки препинания при сравнении строк.

console.log("hello!".localeCompare("hello", "en", { ignorePunctuation: true })); // 0
console.log("word, word".localeCompare("word word", "en", { ignorePunctuation: true })); // 0

Этот параметр полезен при обработке текста, если знаки препинания не должны влиять на порядок сортировки.

Применение localeCompare на практике (RU регион)

Метод 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 — мощный инструмент для локализованного сравнения строк, он имеет ряд ограничений, о которых важно знать при использовании в реальных проектах.

Производительность на больших объемах данных

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, предварительную нормализацию данных или специализированные библиотеки.

Источник: https://habr.com/ru/companies/gnivc/articles/881450/


Интересные статьи

Интересные статьи

Привет, Хаброжители! Представьте, что вы не просто программируете, а создаете элегантные решения, обладая глубоким пониманием алгоритмов и структур данных. Откройте же мощь алгоритмического мышлен...
Меня зовут Андрей Буйновский, я разработчик в ИТ-компании. Мы с командой решили написать эту статью и поделиться некоторыми проблемами, подводными камнями и их решениями в разработке мобильного прилож...
Добавляем функционал быстрых ответов на любом тарифе Битрикс24Как добавить одну из самых полезных функций для чатов открытых линий для любого коммерческого тарифа? Все очень просто, нужно лишь..... ...
Как ускорить 1С БИТРИКС и снизить кол-во запросов к БД используя ядро D7.Пример выборки элементов IBlock с пользовательскими свойствами в один запрос.
Aarch64 — это 64-битная архитектура от ARM (иногда её называют arm64). В этой статье я расскажу, чем она отличается от "обычных" (32-битных) ARM и насколько сложно портировать на него свою систе...