PHP の多次元配列を「要素数」でソートする(個数順に並び替える)


PHP の多次元配列をキーや値でなく、要素の「個数」でソートしたい

Qiita 記事を「php 多次元配列 要素数でソート」でググってもタイトルからピンポイントでヒットしなかったので、自分のググラビリティとして。

元のデータ
Array
(
    [5] => Array <----------------- 要素数 4
        (
            [0] => 5
            [1] => 5
            [2] => 8
            [3] => 9
        )

    [0] => Array <----------------- 要素数 5
        (
            [0] => 4
            [1] => 3
            [2] => 2
            [3] => 1
            [4] => 0
        )

    [10] => Array <---------------- 要素数 1
        (
            [0] => 10
        )

)
希望するソート(要素数順)
Array
(
    [0] => Array
        (
            [0] => 4
            [1] => 3
            [2] => 2
            [3] => 1
            [4] => 0
        )

    [5] => Array
        (
            [0] => 5
            [1] => 5
            [2] => 8
            [3] => 9
        )

    [10] => Array
        (
            [0] => 10
        )

)

TL;DR

キー(連想インデックス)は再採番されるタイプ
usort($list_item, function ($a, $b) {
    return count($a) < count($b);    // 降順
    // return count($a) > count($b); // 昇順
});

多次元配列で、連想配列の添字(連想インデックス)もキープしたい場合は uasort を利用。

キー(連想インデックス)も保持したい場合
uasort($list_item, function ($a, $b) {
    return count($a) < count($b);    // 降順
    // return count($a) > count($b); // 昇順
});

TS;DR

とある画像処理で、配列にオブジェクトを突っ込み、突っ込まれたオブジェクト数が多い順に要素をソートする必要がありました。

画像全体の色味(占領している色)を調べるためです。例えば「肌色の出現が多い」「赤系統が多い」などです。

横方向、縦方向の FOR ループで画像全体をなめながら、RGB 値をキーにしてピクセル情報をもったクラス・オブジェクトを追加して行きました。

こんなイメージ
for ($y = 0; $y < $height_image; $y++) {
    for ($x = 0; $x < $width_image; $x++) {
        $rgb = imagecolorat($res_image, $x, $y);
        $list_color[$rgb][] = new MyPixel($rgb);
    }
}

これにより、オブジェクト数が多い要素のキーを確認すれば、全体の色味も検知できるというわけです。(実際には RGB 値でなく HSV 値に変換しています)

しかし、キーや値での配列のソートはわかるものの「要素数でソート」に悩んでしまいました。

たまにしか使わない array_walk() とか使うんだろうな、と。いや、おそらくもっとシンプルな方法があるはずだ、と。困ったのでググって Qiita のですが、なかなかタイトルから一発でわかる記事がありませんでした。

すると「PHPの多次元連想配列のソート」の Qiita 記事のコメント欄に GURU がわかりやすい方法を教えてくれていました。

PHP5.3 以降であればクロージャを使って usort でもいけます。こちらは多次元配列だけでなくオブジェクトの配列にも柔軟に対応出来るのが強みです。

usort($array, function ($a, $b) {
    return $a['track_num'] - $b['track_num'];
});

PHPの多次元連想配列のソート | コメント @ Qiita より)

最終的にこんなイメージで落ち着きました。バッチリ Qiita !

// GD で PNG 画像のリソースを取得
$res_image = imagecreatefrompng('sample.png');

// 画像のサイズ取得
$width_image  = imagesx($res_image);
$height_image = imagesy($res_image);

// 縦方向のループ
for ($y = 0; $y < $height_image; $y++) {
    // 横方向のループ
    for ($x = 0; $x < $width_image; $x++) {
        // 各ピクセルの RGB 値を取得
        $rgb = imagecolorat($res_image, $x, $y);
        // 配列にピクセル情報用のオブジェクトを追加
        $list_color[$rgb][] = new MyPixel($rgb);
    }
}

// $list_color の1階層目の要素内にある要素数で降順ソート(添字キー保持あり)
uasort($list_color, function ($a, $b) {
    return count($a) < count($b); // 降順
});

参考文献