🔼

PHP で二次元配列を特定の値でソートする

2017/03/23に公開

たまに自分で使うのでコピペ用。

sample

たとえば以下のような配列があるとする。

$array = array(
    array(
        'id'       => 1,
        'name'     => '名前です',
        'birthday' => '1999-07-31',
        'sort'     => 20,
    ),
    array(
        'id'       => 13,
        'name'     => '名前だろ',
        'birthday' => '1984-11-26',
        'sort'     => 10,
    ),

    array(
        'id'       => 21,
        'name'     => '名前じゃん',
        'birthday' => '1985-01-23',
        'sort'     => 1,
    ),
);

この配列を sort 順に並べ替えたいという、 DB からデータを取ってきた後に良くある問題(この程度の場合は SQL の段階で order by すればいいけど)。

実装

配列を独自の仕様で並べ替えをしたい場合は usort を使うというのは常識。
usort に与える関数を以下のようなクロージャを用意することで、使い回しが効くようになる。

// 数値ソート用の関数
$numeric_sort = function( $field ) {
    // クロージャにする
    return function( $a, $b ) use ( $field ) {
        // 実際の比較部分
        if ( $a[$field] == $b[$field] ) {
            return 0;
        }
        return $a[$field] < $b[$field] ? -1 : 1;
    };
};

// ソート
usort( $array, $numeric_sort( 'sort' ) );

文字列問題

しかし上記は数値しか考慮してない、 PHP.net あたりに載っている usort の教科書的サンプル風。
実際は名前順とかも発生するので以下のようにする必要がある。

// 文字列辞書順ソート
$alpha_sort = function( $field ) {
    // クロージャにする
    return function( $a, $b ) use ( $field ) {
        return strcmp( $a[$field], $b[$field] );
    };
};

// ソート
usort( $array, $alpha_sort( 'name' ) );

数値の場合も(特定のケースを除いて)こちらで十分機能する。
※ 16進数や null, false なんかも比較できてしまうので、そういう値が混在することがあらかじめわかっている時は使えない。

比較対象欠損対策

なお、場合によっては配列内に指定のキーが存在しない場合もあるので、その場合の対策が必要になる。

// 空対策した文字列辞書順ソート
$sorter = function( $field ) {
    // クロージャにする
    return function( $a, $b ) use ( $field ) {
        if ( empty( $a[$field] ) && empty( $b[$field] ) ) {
            // どっちもない -> 等しいとみなす
            return 0;
        } elseif ( empty( $a[$field] ) ) {
            // 先に投入された方がない -> 後の値が大きいとみなす
            return -1;
        } elseif ( empty( $b[$field] ) ) {
            // 後に投入された方がない -> 先の値が大きいとみなす
            return 1;
        }
        return strcmp( $a[$field], $b[$field] );
    };
};

// ソート
usort( $array, $sorter( 'name' ) );

一言

宇宙船演算子使いたい。

Discussion