PHP

PHP: 文字列リストを並び替える

この投稿中のコードはすべて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","ぁぃ","あい","アイ","うえ","亜"]

余談: JavaScriptの場合

Intl.Collator

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です