ソート時に大文字と小文字のどちらを先に表示するかを制御する方法

文字列をソートする際の大文字小文字の順序を決定するには、Intl.CollatorのcaseFirstオプションを使用します

はじめに

大文字と小文字だけが異なる文字列の配列をソートする場合、JavaScriptはどのバージョンを先に配置するかを決定する必要があります。appleAppleの前に来るべきか、それともAppleappleの前に来るべきでしょうか?アプリケーションによって、この順序付けに対する要件は異なります。

デフォルトでは、大文字と小文字だけが異なる文字列のソート順序は、ロケールと感度の設定によって決まります。多くの場合、appleAppleのような文字列は同等として扱われ、その順序は予測不可能になります。アプリケーションでケースの順序が重要な場合は、大文字と小文字のどちらを先にソートするかを明示的に制御する必要があります。

Intl.Collator APIは、ケースの順序を決定するcaseFirstオプションを提供しています。コレーターを設定して、大文字を小文字の前に配置するか、小文字を大文字の前に配置するか、またはロケールのデフォルトの動作を使用するかを指定できます。このレッスンでは、caseFirstオプションの動作、効果がある場合、および実際のソートシナリオでの使用方法について説明します。

caseFirstオプションの機能

caseFirstオプションは、大文字と小文字だけが異なる文字列の順序を制御します。このオプションは3つの文字列値を受け付けます:"upper""lower"、または"false"です。

"upper"に設定すると、大文字は対応する小文字の前にソートされます。文字列Appleappleの前に来ます。

"lower"に設定すると、小文字は対応する大文字の前にソートされます。文字列appleAppleの前に来ます。

"false"(ブール値ではなく文字列値)に設定すると、コレーターはロケールのデフォルトのケース順序を使用します。これは、オプションを指定しない場合のデフォルトの動作です。

caseFirstオプションは、大文字と小文字だけが異なる文字列にのみ影響します。基本的な文字が異なる文字列は、この設定に関係なく、通常のアルファベット順のルールに従ってソートされます。

デフォルトの大文字小文字の順序の仕組み

caseFirstオプションを指定しない場合、照合順序はロケールと感度設定に依存するデフォルトの動作を使用します。

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

words.sort(collator.compare);

console.log(words);
// 出力: ['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);
// 出力: ['Apple', 'apple', 'Banana', 'banana']

照合順序はまず基本文字でアルファベット順にソートし、次に各グループ内で大文字小文字の順序を適用します。aで始まる単語の中では、Appleappleの前に来ます。bで始まる単語の中では、Bananabananaの前に来ます。

この順序付けは、固有名詞や大文字で始まるタイトルを一般名詞よりも目立つように前に表示するリストを表示する場合に便利です。

const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const names = ['smith', 'Smith', 'jones', 'Jones'];

names.sort(collator.compare);

console.log(names);
// 出力: ['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);
// 出力: ['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);
// 出力: ['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);
// 出力はロケールの実装に依存します

出力順序は、ロケールが標準と見なすものによって異なります。一部のロケールでは大文字が先、他のロケールでは小文字が先と定義されており、大文字小文字の異なる文字列を同等として扱うロケールもあります。

このオプションは、設定の選択を誤って省略するのではなく、ロケールのデフォルトを使用していることを明示したい場合に便利です。

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);
// 出力: ['apple', 'Apple', 'APPLE'] (または任意の順序)

ベース感度では、コレーターは3つの文字列をすべて同等として扱います。大文字小文字がまったく考慮されないため、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);
// 出力: ['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);
});
// 出力:
// 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);
// 出力: ['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);
// 出力: ['Item1', 'item1', 'Item2', 'item2', 'Item10', 'item10']

コレーターは数値部分に数値比較を適用し、文字部分に大文字小文字の順序付けを適用します。アイテムはまず数値(1、2、10)でソートされ、次に各数値グループ内で大文字小文字によってソートされます。

caseFirstのためのUnicode拡張キーの使用

caseFirstオプションは、kfキーを使用してロケール識別子のUnicode拡張でも設定できます。拡張値はupperlower、およびfalseです。

const collator = new Intl.Collator('en-US-u-kf-upper');
const words = ['apple', 'Apple', 'banana', 'Banana'];

words.sort(collator.compare);

console.log(words);
// 出力: ['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);
// 出力: ['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);
// 出力: "upper"

これは、特にUnicode拡張とオプションオブジェクトを組み合わせる場合に、コレーターが期待通りの設定になっているかをデバッグまたは確認するのに役立ちます。

const collator = new Intl.Collator('en-US-u-kf-lower');
const options = collator.resolvedOptions();

console.log(options.caseFirst);
// 出力: "lower"

解決されたオプションは、オプションオブジェクトでオーバーライドされていない場合、Unicode拡張設定を反映します。

各caseFirst値を使用するタイミング

以下の場合にcaseFirst: "upper"を使用します:

  • 固有名詞やタイトルが一般的な単語の前に表示されるようにしたい場合
  • アプリケーションの規則で大文字のテキストをより重要なものとして扱う場合
  • 大文字バージョンが正規形式である名前をソートする場合

以下の場合にcaseFirst: "lower"を使用します:

  • 小文字のテキストが標準形式として最初に表示されるようにしたい場合
  • アプリケーションの規則で小文字をデフォルトとし、大文字を特別なものとして扱う場合
  • 小文字がより一般的な技術的識別子をソートする場合

以下の場合にcaseFirst: "false"を使用します:

  • ロケール固有の規則に従いたい場合
  • 大文字小文字が異なる文字列の順序がアプリケーションにとって重要でない場合
  • オプションを誤って省略するのではなく、デフォルトを使用していることを明示したい場合