JavaScriptのsort関数で日本語を50音順に並べたい

# 目的
JavaScriptで日本語でのソートを行う際、意図した挙動にならなかったため自分なりに調べた結果をまとめました。

今回はこんな感じで単純な文字列をソートしていきます。
```javascript
const array = ["う", "い", "あ", "ウ", "イ", "ア", "宇", "井", "亜", "a", "i", "u"];
```

ひらがな、カタカナ、漢字、英語が混じった配列ですね。

# 単純なソート関数
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

JavaScriptの組み込み関数であるsort関数を使う場合、以下のようになります。

```javascript
const array = ["う", "い", "あ", "ウ", "イ", "ア", "宇", "井", "亜", "a", "i", "u"];

array.sort();
console.log(array);
// 出力結果 => [a,i,u,あ,い,う,ア,イ,ウ,井,亜,宇]
```

並び順が **英語 -> ひらがな -> カタカナ -> 漢字** となりました。
漢字に至っては50音順にすらなっていませんね。

:::note info
sort( )を使用した場合、並び順は以下のようになる
**英語 -> ひらがな -> カタカナ -> 漢字**
:::

### ソートの順番について
ソートの順番がどのように決まるかの補足説明になります。
ドキュメントには以下のように記載されています。

> ソート順を定義する関数を指定します。省略された場合、配列の各要素は文字列に変換され、各文字の Unicode コードポイント順に従ってソートされます。

ここで、それぞれの文字のUnicode コードポイントを確認してみましょう。

```javascript
const array = ["う", "い", "あ", "ウ", "イ", "ア", "宇", "井", "亜", "a", "i", "u"];

const point = array.map(n => n.codePointAt())
console.log(point});
// 出力結果 => [12358,12356,12354,12454,12452,12450,23431,20117,20124,97,105,117]
``` 

つまり、sort関数では **英語 -> ひらがな -> カタカナ -> 漢字** と意図的にグルーピングされているというわけでなく、
単純にポイント順に並び替えが行われているということです。

:::note info
sort( )を使用した場合、並び順は**Unicodeコードポイント順**となる
:::

# 日本語に準じた50音順でソート
日本語の50音順にソートしたい場合は**localeCompare**や**Intl.Collator**を利用します。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator

```javascript:localeCompare
const array = ["う", "い", "あ", "ウ", "イ", "ア", "宇", "井", "亜", "a", "i", "u"];

array.sort((a, b) => {
  return a.localeCompare(b, 'ja');
});
console.log(array);
// 出力結果 => [a,i,u,あ,ア,い,イ,う,ウ,亜,井,宇]
```

```javascript:Intl.Collator
const array = ["う", "い", "あ", "ウ", "イ", "ア", "宇", "井", "亜", "a", "i", "u"];

const collator = new Intl.Collator("ja");
array.sort(collator.compare);
console.log(array);
// 出力結果 => [a,i,u,あ,ア,い,イ,う,ウ,亜,井,宇]
```

並び順がUnicodeコードポイント順ではなく、**英語 -> ひらがな&カタカナ -> 漢字** 順になりましたね。

:::note info
sort( )を使用する際、**localeCompare**や**Intl.Collator**を使うことによって
50音順に並べることができる
:::

# ホントに50音順といえるのか
ふと、50音順と言われた場合の出力結果が **[a, i, u, あ, ア, い, イ, う, ウ, 亜, 井, 宇]** で納得できるか疑問に思いました。
英語は別として、やはり私たちの日常生活では50音順と言われれば以下のような結果を予測しますよね。
- **[あ, ア, 亜, い, イ, 井, う, ウ, 宇]**

しかし**漢字を含めた場合、そこまでの完璧なソートは無理**なようです。
考えてみれば当たり前のことかもしれません。漢字には様々な「読み」が存在しますもんね。
（人名であればなおさら、同じ漢字なのに読みが異なる苗字なんて沢山ありますしね。。。）

もし、自分が思う読み通りのソートを行いたい場合、予め振り仮名属性を持たせておく必要があります。
なので、人名をソートしたい場合なんかも意図しない並びになることが多々あるかと思います。

```javascript
const array = ["平田", "鈴木", "石井", "田中"];
// [石井（いしい）, 鈴木（すずき）, 田中（たなか）, 平田（ひらた）] の順になってほしい

// sort関数
array.sort();
console.log(array);
// [平田,田中,石井,鈴木]

// localeCompare
array.sort((a, b) => {
  return a.localeCompare(b, 'ja');
});
console.log(array)
// [石井,田中,平田,鈴木]

// Intl.Collator
const collator = new Intl.Collator("ja");
array.sort(collator.compare);
console.log(array)
// [石井,田中,平田,鈴木]
```

:::note info
**漢字を含む場合、思い通りに並び替えるのは難しい**
漢字だけでなく振り仮名を持たせ、それをもとにソートを行うとよい
:::

# まとめ
ここまでで、sort( )の挙動を確認できました。
単純な数値や英語だけのソートならまだしも、日本語（ひらがな・カタカナ・漢字）を含めると意外と奥が深いことがわかりました。

ここら辺をさらに深ぼるには文字コードとか色々と関わってきそうですね。
今回はこの辺で。。。