如何控制排序时大写字母或小写字母优先

使用 Intl.Collator 的 caseFirst 选项来确定字符串排序时的大小写顺序

简介

当你对仅大小写不同的字符串数组进行排序时,JavaScript 需要决定哪种形式排在前面。是 apple 应该排在 Apple 前面,还是 Apple 应该排在 apple 前面?不同的应用对这种排序顺序有不同的需求。

默认情况下,仅大小写不同的字符串的排序顺序取决于区域设置和敏感度设置。在许多情况下,像 appleApple 这样的字符串会被视为相等,导致它们的顺序不可预测。如果你的应用对大小写排序有要求,就需要明确控制大写字母还是小写字母优先。

Intl.Collator API 提供了 caseFirst 选项,用于决定大小写排序顺序。你可以配置排序器让大写字母排在小写字母前、小写字母排在大写字母前,或者使用区域设置的默认行为。本教程将介绍 caseFirst 选项的工作原理、其生效条件,以及如何在实际排序场景中使用它。

caseFirst 选项的作用

caseFirst 选项用于控制仅大小写不同的字符串的排序顺序。它接受三个字符串值:"upper""lower""false"

当设置为 "upper" 时,大写字母会排在对应的小写字母前。例如,字符串 Apple 会排在 apple 前面。

当设置为 "lower" 时,小写字母会排在其大写字母之前。例如,字符串 apple 会排在 Apple 之前。

当设置为 "false"(字符串值,而不是布尔值)时,排序器会使用该语言环境的默认大小写顺序。如果未指定该选项,则这是默认行为。

caseFirst 选项只影响仅大小写不同的字符串。对于基础字母不同的字符串,无论该设置如何,都会按照正常的字母顺序排序。

默认大小写顺序的工作原理

如果未指定 caseFirst 选项,排序器会根据语言环境和敏感度设置采用默认行为。

const collator = new Intl.Collator('en-US');
const words = ['apple', 'Apple', 'APPLE'];

words.sort(collator.compare);

console.log(words);
// Output: ['apple', 'Apple', 'APPLE']

输出顺序取决于具体实现和语言环境。在默认敏感度设置下,排序器会考虑大小写差异,但 appleAppleAPPLE 的具体排序顺序会有所不同。

不同的语言环境有不同的默认大小写排序习惯。有些语言环境习惯将小写字母排在前面,而有些则将大写字母排在前面。依赖默认行为会导致在不同环境下结果不一致。

使用 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']

排序器会先按基础字母进行字母顺序排序,然后在每组内应用大小写顺序。在以 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: "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)

在基础敏感度下,collator 会将这三个字符串视为相等。由于大小写完全不被考虑,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']

在 variant 敏感度下(会考虑所有差异,包括大小写),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

collator 会将首字母大写的名称排在小写版本之前。这种排序方式有助于在混合数据集中区分专有名词和普通词汇。

以受控大小写顺序排序文件名

文件系统的大小写敏感行为各不相同。有些将大写和小写视为相同,有些则区分。显式指定大小写顺序可确保无论底层文件系统如何,排序后的文件列表都能符合用户预期。

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']

collator 会对数字部分进行数值比较,对字母部分应用大小写排序。各项会先按数值(1、2、10)排序,再在每个数字组内按大小写排序。

使用 Unicode 扩展键设置 caseFirst

可以通过在区域标识符中使用 kf 键的 Unicode 扩展来设置 caseFirst 选项。扩展值包括 upperlowerfalse

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"

  • 你希望遵循特定语言环境的规范
  • 你的应用对大小写不同的字符串顺序没有要求
  • 你希望明确表示正在使用默认值,而不是因疏忽遗漏该选项