はじめに|この記事で得られる価値
「JavaScriptで五十音順に並べたはずなのに、順番がバラバラ……」
そんな場面で焦った経験はありませんか?
私たちも、一覧画面のソート実装で
同じ壁にぶつかることがあります。
この記事では、日本語特有の並び順の難しさを整理しながら、
実務でそのまま使いやすい比較方法を解説します。
特に押さえたいのは、次の3点です。
・=== と == の違い
・localeCompare() と Intl.Collator の使い分け
・normalize(“NFC”) による表記揺れ対策
「理屈はあとでいいから、まず正しく動く形を知りたい」
そんなときの入口として読める構成にしています。
1. なぜ === を基本にするのか
文字列の一致判定では、まず === を使いましょう。
理由はとてもシンプルです。
== は比較時に、自動で型変換を行うからです。
この挙動が、思わぬ不具合の入口になります。
たとえば、文字列の “1” と数値の 1 は、
== だと true になります。
console.log("1" == 1); // true
console.log("1" === 1); // false
実務では「見た目が同じ」よりも、
「型まで一致している」ことが重要です。
一覧画面や検索条件の比較でも、
曖昧さを残さないほうが安全です。
厳密等価演算子
値だけでなく、型まで含めて一致しているかを判定する演算子です。等価演算子
型が違っていても、自動変換したうえで比較する演算子です。暗黙の型変換
実行時にデータ型が自動で変わることです。意図しない一致の原因になります。
2. なぜ > や < では日本語ソートが崩れるのか
「文字列なら a > b で比較できるのでは?」
そう考えたくなりますが、日本語では注意が必要です。
比較演算子は、基本的に
文字の並びをコード順で判定します。
この順番は、私たちが期待する
五十音順や辞書順とは一致しません。
そのため、次のようなズレが起こります。
- ひらがなとカタカナが直感どおりに並ばない
- 半角カタカナと全角カタカナが混在すると崩れる
- 数字を含む文字列が自然な順番にならない
たとえば、ユーザーが求めているのは
「辞書で見る並び」に近い順序です。
一方で、単純比較は
「内部的な文字コードの順序」でしかありません。
この差を理解しておくと、
レビューでも説明しやすくなります。
Unicodeコードポイント
文字ごとに割り当てられた番号です。番号順は、必ずしも日本語の辞書順ではありません。
3. 日本語比較の基本は localeCompare() と Intl.Collator
では、どうやって日本語を自然に比較すればよいのでしょうか?
ここで使うのが、言語ルールを考慮できる比較機能です。
代表的なのは localeCompare() と Intl.Collator です。
■ 手軽に試せる localeCompare()
まず試しやすいのが localeCompare() です。
文字列同士を、指定したロケールで比較できます。
const list = ["い", "あ", "う"];
list.sort((a, b) => a.localeCompare(b, "ja"));
// ["あ", "い", "う"]
小規模な配列なら、これでも十分です。
「まず正しく並べたい」という場面で役立ちます。
ただし、ソート件数が増えると
比較回数も一気に増えます。
その結果、毎回の比較コストが効いてきて、
画面の体感速度に影響することがあります。
■ 実務で使いやすい Intl.Collator
大量データを扱うなら、私たちは Intl.Collator をおすすめします。
理由は、一度作った比較設定を使い回せるからです。
const collator = new Intl.Collator("ja");
const list = ["い", "あ", "う"];
list.sort(collator.compare);
// ["あ", "い", "う"]
実務で使いやすい理由は、主に3つあります。
- 比較設定を再利用できる
- 数値混じりの自然順ソートにも対応できる
- 一覧画面のような繰り返し処理で安定しやすい
「正しさ」と「扱いやすさ」を両立したいとき、
まず候補にしたい方法です。
String.prototype.localeCompare()
文字列をロケールの規則に従って比較するメソッドです。Intl.Collator
多言語の文字列比較を効率よく行うためのオブジェクトです。メモリ効率
同じ処理を無駄なく再利用し、負荷を抑えやすい状態を指します。
関連して、ブラウザ負荷の見方を整理したい場合は、
『パフォーマンスタブ徹底解説|Chrome DevToolsでWebパフォーマンス改善の全手順』も参考になります。
4. normalize(“NFC”) が表記揺れ対策で効く理由
「見た目は同じなのに、一致しない……」
この現象も、日本語比較ではよくあります。
代表例が、濁点を含む文字です。
たとえば「ガ」は、内部的には複数の表現がありえます。
- 1文字として表現された「ガ」
- 「カ」と濁点を組み合わせた「ガ」
見た目は同じでも、
そのまま比較すると一致しないことがあります。
const str1 = "ガ";
const str2 = "カ\u3099";
console.log(str1 === str2); // false
console.log(str1.normalize("NFC") === str2.normalize("NFC")); // true
ここで役立つのが normalize(“NFC”) です。
比較前に表現形式をそろえることで、表記揺れを吸収できます。
特に、次のような入力元では有効です。
- ユーザー入力
- 外部APIのレスポンス
- CSVやファイル経由の文字列
「表示は同じなのに検索にヒットしない」
そんな不具合を減らすうえで、とても実用的です。
Unicode正規化(NFC)
文字の内部表現を一定の形式にそろえる処理です。合成済み文字 / 結合文字
1文字として持つ表現と、複数文字の組み合わせで持つ表現のことです。
5. 実務で使いやすい日本語比較テンプレート
ここまでの内容を踏まえて、
すぐ使いやすい形を1つにまとめます。✅
ポイントは、次の2つです。
- Intl.Collator を使い回す
- 比較前に normalize(“NFC”) でそろえる
/**
* 日本語の辞書順ソート用コンパレータ
* - Intl.Collatorを再利用して高速化
*/
const japaneseCollator = new Intl.Collator("ja", {
numeric: true,
sensitivity: "accent",
});
/**
* 安全な文字列比較関数
*/
const compareJapanese = (a, b) => {
const cleanA = (a ?? "").normalize("NFC");
const cleanB = (b ?? "").normalize("NFC");
return japaneseCollator.compare(cleanA, cleanB);
};
// 使用例
const items = ["第10集", "第2集", "ガンダム", "ガンダム"];
items.sort(compareJapanese);
このテンプレートなら、
数値混じりの並びにも対応しやすくなります。
さらに、濁点の表記差も吸収できるため、
実務の一覧ソートで使い回しやすい形です。
Intl.Collator を関数の外で作るのも重要です。
毎回生成しないぶん、無駄な負荷を減らせます。
undefined の混入で困る場面が多い場合は、
『なぜ「undefined」が出る?原因と解決法10選|JavaScript のデバッグに効く実例集』もあわせて確認してみてください。
コンパレータ
2つの値の順序を決めるための比較関数です。自然順ソート
1, 2, 10 のように、数字を数値として自然に並べる方法です。オーバーヘッド
本来の処理に加えて発生する追加コストのことです。
6. まず押さえたい実務判断のまとめ
ここまでを、現場目線で整理すると次のようになります。
- 一致判定は === を使う
- 日本語ソートは Intl.Collator を使う
- 比較前に normalize(“NFC”) を通す
- 数値混じりなら numeric: true を付ける
「どれを選べばよいか迷う」という状態なら、
まずはこの組み合わせで十分戦えます。
実装の正しさだけでなく、
レビューで説明しやすいのも大きな利点です。
FAQ|よくある疑問
■ Object.is() を使うべき場面はありますか?
通常の文字列比較なら、基本は === で問題ありません。
NaN や +0 / -0 のような特殊ケースを厳密に扱いたいときに使います。
■ 漢字も五十音順にきれいに並びますか?
標準機能だけでは、漢字の「読み」までは判断できません。
厳密な五十音順が必要なら、フリガナや読み仮名データを持たせる設計が必要です。
■ パフォーマンス重視なら結論は何ですか?
結論としては、Intl.Collator の再利用が第一候補です。
Reactなら useMemo などで保持し、毎回作り直さない構成が扱いやすいです。
Object.is()
=== に近い比較を行いながら、一部の特殊値をより厳密に判定できるメソッドです。useMemo
計算結果や生成した値を保持し、再計算を減らすための仕組みです。
7. まとめ|迷ったらこの形で始めましょう
JavaScriptの文字列比較は、
一見シンプルでも実務では落とし穴があります。
特に日本語ソートでは、
単純比較だけでは期待どおりに並びません。
だからこそ、次の流れを基準にすると安心です。
- 一致判定は ===
- 並び替えは Intl.Collator
- 表記揺れ対策は normalize(“NFC”)
まずは手元の一覧画面や検索機能で、
このテンプレートをそのまま試してみてください。
関連して、実務で使えるJavaScriptの考え方を広げたい場合は、
『JavaScript中級者へのステップアップ|実務で使えるテクニック10選』も参考になります。
ぜひコードをコピペして、まずは動かしてみてください。