Как управлять порядком сортировки: сначала заглавные или строчные буквы
Используйте опцию caseFirst в Intl.Collator, чтобы задать порядок букв при сортировке строк
Введение
Когда вы сортируете массив строк, отличающихся только регистром, JavaScript должен определить, какая версия будет первой. Должна ли apple идти перед Apple, или наоборот — Apple перед apple? В разных приложениях требования к такому порядку могут отличаться.
По умолчанию порядок сортировки строк, различающихся только регистром, зависит от локали и настроек чувствительности. Во многих случаях строки вроде apple и Apple считаются равными, и их порядок становится непредсказуемым. Если для вашего приложения важен порядок по регистру, нужно явно указать, что должно идти первым — заглавные или строчные буквы.
API Intl.Collator предоставляет опцию caseFirst, которая определяет порядок по регистру. Вы можете настроить collator так, чтобы сначала шли заглавные буквы, сначала строчные, либо использовать поведение по умолчанию для локали. В этом уроке объясняется, как работает опция caseFirst, когда она влияет на сортировку и как её применять на практике.
Как работает опция caseFirst
Опция caseFirst управляет порядком строк, отличающихся только регистром. Она принимает три строковых значения: "upper", "lower" или "false".
Если указано "upper", то заглавные буквы идут перед строчными. Строка Apple будет перед apple.
Если установить значение "lower", строчные буквы будут идти перед заглавными. Строка apple будет располагаться перед Apple.
Если установить "false" (строковое значение, а не булево), collator будет использовать стандартный для локали порядок сортировки по регистру. Это поведение по умолчанию, если вы не указываете этот параметр.
Параметр caseFirst влияет только на строки, которые отличаются только регистром. Строки, различающиеся по базовым буквам, сортируются по обычным алфавитным правилам независимо от этой настройки.
Как работает сортировка по умолчанию по регистру
Если не указывать параметр caseFirst, collator использует поведение по умолчанию, которое зависит от локали и настроек чувствительности.
const collator = new Intl.Collator('en-US');
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'APPLE']
Порядок вывода зависит от реализации и локали. При стандартных настройках чувствительности collator учитывает различия в регистре, но конкретный порядок для apple, Apple и APPLE может отличаться.
В разных локалях используются разные правила сортировки по регистру. В некоторых локалях строчные буквы идут первыми, в других — заглавные. Опираться на поведение по умолчанию не стоит, так как оно может отличаться в разных средах.
Как сделать так, чтобы заглавные буквы шли первыми с помощью caseFirst upper
Установка caseFirst: "upper" гарантирует, что заглавные буквы всегда будут идти перед строчными в отсортированных результатах.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['Apple', 'apple', 'Banana', 'banana']
Collator сначала сортирует по базовой букве, а затем применяет сортировку по регистру внутри каждой группы. Среди слов, начинающихся на a, Apple будет идти перед apple. Среди слов, начинающихся на b, Banana будет идти перед banana.
Такой порядок удобен при отображении списков, где имена собственные или заглавные названия должны выделяться и идти перед обычными существительными.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const names = ['smith', 'Smith', 'jones', 'Jones'];
names.sort(collator.compare);
console.log(names);
// Output: ['Jones', 'jones', 'Smith', 'smith']
Имена с заглавной буквы идут перед словами с маленькой буквы, что помогает легче отличать имена собственные от обычных слов в смешанных списках.
Сначала строчные буквы с caseFirst lower
Установка caseFirst: "lower" гарантирует, что строчные буквы всегда будут идти перед заглавными в отсортированных результатах.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'banana', 'Banana']
Сначала сортировка идёт по алфавиту, а внутри каждой группы сначала идут слова со строчной буквы, затем — с заглавной.
Такой порядок соответствует традициям, где текст со строчной буквы считается более стандартным или нейтральным, а заглавные буквы используются для выделения или особых случаев.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const tags = ['javascript', 'JavaScript', 'python', 'Python'];
tags.sort(collator.compare);
console.log(tags);
// Output: ['javascript', 'JavaScript', 'python', 'Python']
Сначала идут теги со строчной буквы, что удобно, если строчный вариант считается каноническим, а заглавные — менее распространёнными вариантами.
Использование caseFirst false для поведения по умолчанию в локали
Установка caseFirst: "false" (строковое значение) явно указывает использовать порядок букв по умолчанию для локали. Это даст тот же результат, что и если не указывать этот параметр вообще.
const collator = new Intl.Collator('en-US', { caseFirst: 'false' });
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output depends on locale implementation
Порядок вывода зависит от того, что считается стандартом в данной локали. В некоторых локалях сначала идут заглавные, в других — строчные, а где-то регистр вообще не учитывается.
Эта опция полезна, если вы хотите явно указать, что используете настройки по умолчанию для локали, а не случайно забыли про конфигурацию.
Когда caseFirst не влияет на результат
Опция caseFirst влияет на сравнение только если настройка sensitivity позволяет учитывать регистр. При sensitivity: "base" или sensitivity: "accent" регистр полностью игнорируется, и опция caseFirst становится неактуальной.
const collator = new Intl.Collator('en-US', {
sensitivity: 'base',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'APPLE'] (or any order)
При базовой чувствительности компаратор считает все три строки равными. Опция caseFirst не влияет на результат, потому что регистр вообще не учитывается. Алгоритм сортировки сохраняет исходный порядок равных элементов (стабильная сортировка), поэтому результат совпадает с порядком во входных данных.
Чтобы опция caseFirst имела эффект, используйте sensitivity: "case" или sensitivity: "variant" (по умолчанию).
const collator = new Intl.Collator('en-US', {
sensitivity: 'variant',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['APPLE', 'Apple', 'apple']
При чувствительности к вариантам (когда учитываются все различия, включая регистр) опция caseFirst определяет порядок строк, отличающихся только регистром.
Сортировка имён с единообразным порядком по регистру
При сортировке имён пользователей или других собственных имён единообразный порядок по регистру обеспечивает предсказуемый результат во всём приложении.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const users = [
{ name: 'alice' },
{ name: 'Alice' },
{ name: 'bob' },
{ name: 'Bob' }
];
users.sort((a, b) => collator.compare(a.name, b.name));
users.forEach(user => {
console.log(user.name);
});
// Output:
// Alice
// alice
// Bob
// bob
Компаратор размещает имена с заглавной буквы перед вариантами с маленькой буквы. Такой порядок помогает легче отличать собственные имена от обычных слов в смешанных наборах данных.
Сортировка имён файлов с контролем порядка по регистру
Файловые системы по-разному обрабатывают чувствительность к регистру. Некоторые считают прописные и строчные буквы одинаковыми, другие — разными. Явное указание порядка по регистру гарантирует, что отсортированные списки файлов будут соответствовать ожиданиям пользователей независимо от файловой системы.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const files = ['README.md', 'readme.md', 'index.js', 'Index.js'];
files.sort(collator.compare);
console.log(files);
// Output: ['index.js', 'Index.js', 'readme.md', 'README.md']
Сначала идут варианты с маленькой буквы, что соответствует традиции, когда имена файлов в нижнем регистре более стандартны для Unix-подобных систем.
Комбинирование caseFirst с числовой сортировкой
Опция caseFirst работает вместе с другими параметрами сортировки, например numeric. Можно сортировать строки с числами внутри, одновременно управляя порядком по регистру.
const collator = new Intl.Collator('en-US', {
numeric: true,
caseFirst: 'upper'
});
const items = ['item1', 'Item1', 'item2', 'Item2', 'item10', 'Item10'];
items.sort(collator.compare);
console.log(items);
// Output: ['Item1', 'item1', 'Item2', 'item2', 'Item10', 'item10']
Компаратор применяет числовое сравнение к числовым частям и порядок по регистру к буквенным. Элементы сортируются сначала по числовому значению (1, 2, 10), затем по регистру внутри каждой числовой группы.
Использование ключа расширения Unicode для caseFirst
Опцию caseFirst также можно задать через расширение Unicode в идентификаторе локали с помощью ключа kf. Значения расширения: upper, lower и false.
const collator = new Intl.Collator('en-US-u-kf-upper');
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['Apple', 'apple', 'Banana', 'banana']
Строка локали en-US-u-kf-upper указывает английский (США) с сортировкой, где сначала идут заглавные буквы. Это даёт тот же результат, что и передача { caseFirst: 'upper' } в объекте опций.
Если и расширение Unicode, и объект опций задают caseFirst, приоритет имеет объект опций.
const collator = new Intl.Collator('en-US-u-kf-upper', {
caseFirst: 'lower'
});
const words = ['apple', 'Apple'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple']
Объект опций задаёт caseFirst: 'lower', что переопределяет расширение kf-upper в строке локали. Коллатор использует сортировку, где сначала идут строчные буквы.
Как проверить, какое значение caseFirst использует коллатор
Метод resolvedOptions() возвращает фактические опции, которые использует коллатор, включая настройку caseFirst.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// Output: "upper"
Это полезно для отладки или проверки, что у вашего коллатора нужная конфигурация, особенно при сочетании расширений Unicode с объектами опций.
const collator = new Intl.Collator('en-US-u-kf-lower');
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// Output: "lower"
Результирующие опции отражают настройку расширения Unicode, если объект опций её не переопределяет.
Когда использовать каждое значение caseFirst
Используйте caseFirst: "upper", когда:
- Вы хотите, чтобы имена собственные или заголовки шли перед обычными словами
- В вашем приложении принято считать заглавный текст более значимым
- Вы сортируете имена, где каноническая форма — с заглавной буквы
Используйте caseFirst: "lower", когда:
- Вы хотите, чтобы сначала шёл текст со строчными буквами как стандартная форма
- В вашем приложении строчные буквы считаются по умолчанию, а заглавные — особенными
- Вы сортируете технические идентификаторы, где чаще встречаются строчные буквы
Используйте caseFirst: "false", когда:
- Вы хотите следовать локальным стандартам
- Порядок строк, различающихся только регистром, не важен для вашего приложения
- Вы хотите явно указать, что используете значения по умолчанию, а не случайно пропустили эту опцию