この投稿中のコードはすべてCC0です
TL;DL
<?php
declare(strict_types=1);
// use Collator;
// この一覧を並び替える
$list = ['あい', 'うえ', 'アイ', 'ABC', 'abc', '亜', 'ぁぃ'];
$collator = Collator::create('ja'); // 実現には Collator を使う
assert($collator !== null);
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON); // ナチュラルソート
$collator->sort($list, Collator::SORT_STRING);
echo json_encode($list, JSON_UNESCAPED_UNICODE) . "\n";
// -> ["abc","ABC","ぁぃ","あい","アイ","うえ","亜"]
説明
プログラムを書いていると、一覧を並び替えることは頻繁にあります。
PHPには組み込みのsort
関数がありますが、このsort
関数の結果は必ずしも求める順序に一致するとは限りません。
例えば、上のコードにある ['あい', 'うえ', 'アイ', 'ABC', 'abc', '亜', 'ぁぃ']
というリストをsort
関数で並び替えると、次のようになります。
<?php
declare(strict_types=1);
$list = ['あい', 'うえ', 'アイ', 'ABC', 'abc', '亜', 'ぁぃ'];
sort($list, SORT_STRING);
echo json_encode($list, JSON_UNESCAPED_UNICODE) . "\n";
// -> ["ABC","abc","ぁぃ","あい","うえ","アイ","亜"]
「なんとなく並んでいればいいよ」というくらいの話であればこれでも良いですが、特にコンピューターに詳しくないユーザーとしては、「アイ」が「うえ」より後にあるのは直感的に良い挙動だとは言えません。
これを「あい、アイ、うえ」の順に並び替えてほしいなという要求はある意味自然なわけです。
これを実現するためには、intlモジュールにあるCollator
クラスを使用し、冒頭のようにコードを書くと良いです。
<?php
declare(strict_types=1);
// use Collator;
// この一覧を並び替える
$list = ['あい', 'うえ', 'アイ', 'ABC', 'abc', '亜', 'ぁぃ'];
$collator = Collator::create('ja'); // 実現には Collator を使う
assert($collator !== null);
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON); // ナチュラルソート
$collator->sort($list, Collator::SORT_STRING);
echo json_encode($list, JSON_UNESCAPED_UNICODE) . "\n";
// -> ["abc","ABC","ぁぃ","あい","アイ","うえ","亜"]
このコード中において $collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
の部分は「ナチュラルソート」を実現するための設定であり、これをONにすることで「1・10・2」ではなく「1・2・10」と並ぶようになります。この設定は好みが分かれるでしょうが、例えばWindowsエクスプローラーはこのようにならびます。PHPの標準関数ならnatsort
を利用するのと同等です。
複雑なソートを行う場合など、usort
関数を適用する場面では、Collator::compare
が使用できます。これはstrcmp
ファミリー相当になります。
たとえば:
<?php
declare(strict_types=1);
// use Collator;
// この一覧を並び替える
$list = ['あい', 'うえ', 'アイ', 'ABC', 'abc', '亜', 'ぁぃ'];
$collator = Collator::create('ja'); // 実現には Collator を使う
assert($collator !== null);
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON); // ナチュラルソート
usort(
$list,
// この例はあまりにシンプルなので $collator->sort() で実現できるが
// 実際のコードでは複合ソートをする場合も多いので $collator->compare() を使用する場合がある
fn (string $a, string $b): int => $collator->compare($a, $b),
);
echo json_encode($list, JSON_UNESCAPED_UNICODE) . "\n";
// -> ["abc","ABC","ぁぃ","あい","アイ","うえ","亜"]