[PHP]自在に多次元配列をソートできるarray_multisort

array_multisort使い方まとめ

急な要件の変更で、SQLで取ったデータを日付は新しい順、価格は高い順みたいに多次元配列を複数条件で並び替える必要が出てしまいました。

PHP側でソートしなければいけない事情があって、一次配列だったらかんたんだけど、多次元でそんなんできるのかなと調べてみたらピッタリの関数を見つけました!

2個でも3個でも好きな条件で多次元配列を複数条件でソートできる、とても便利なarray_multisortの使い方を紹介していきます。

array_multisortの使い方

基本的な書き方 (2つの要素の値をソートするケース)

array_multisortの書き方

array_multisort(
1つ目のソートしたい要素の値が入った配列, 1つ目の並び順の指定, 1つ目のソートする値の型の指定,
2つ目のソートしたい要素の値が入った配列, 2つ目の並び順の指定, 2つ目のソートする値の方の指定,
ソートしたい配列
);

返り値はソートが成功したときは true、失敗したときは falseを返します。

ソート後の配列はそのまま元の配列に再格納されます。

では実際の動作を見ていきましょう。

このような2次元配列があったとします。

$ArrMenu = array (
  array(
    'メニュー番号' => 1
    , '名前' => 'チャーハン'
    , '値段' => '520円'
    , '作成日' => '2018-10-08'
  ),
  array (
    'メニュー番号' => 3
    , '名前' => '天津飯'
    , '値段' => '650円'
    , '作成日' => '2020-02-24'
  ),
  array (
    'メニュー番号' => 2
    , '名前' => '中華飯'
    , '値段' => '650円'
    , '作成日' => '2019-12-15'
  ),
  array (
    'メニュー番号' => 5
    , '名前' => 'マーボー丼'
    , '値段' => '700円'
    , '作成日' => '2019-12-15'
  )
);

この配列を作成日の新しい順、作成日が同じ場合は値段の安いメニュー順で並び替えます。

並べ替えたい要素(この場合’作成日’と’値段’)の値を、事前にそれぞれ別の配列に入れておく必要があります。

作った配列を引数にして、ソートします。

// ソートしたい要素の値を配列に入れる
foreach ($ArrMenu as $Food => $Detail) {
  $ArrDate[] = $Detail['作成日'];
  $ArrPrice[] = $Detail['値段'];
}

// ソートする
array_multisort($ArrDate, SORT_DESC, SORT_NUMERIC, 
$ArrPrice, SORT_ASC, SORT_STRING, 
$ArrMenu);

var_dump($ArrMenu);

狙い通り’作成日’と’値段’でソートができました!

同じ作成日の’中華飯’と’麻婆丼’が値段の安い順で並んでいます。

array(4) {
  [0]=>
    array(4) {
      ["メニュー番号"]=> int(3)
      ["名前"]=> string(9) "天津飯"
      ["値段"]=> string(6) "650円"
      ["作成日"]=> string(16) "2020-02-24"
    }
  [1]=>
    array(4) {
      ["メニュー番号"]=> int(2)
      ["名前"]=> string(9) "中華飯"
      ["値段"]=> string(6) "650円"
      ["作成日"]=> string(16) "2019-12-15"
    }
  [2]=>
    array(4) {
      ["メニュー番号"]=> int(5)
      ["名前"]=> string(15) "マーボー丼"
      ["値段"]=> string(6) "700円"
      ["作成日"]=> string(16) "2019-12-15"
    }
  [3]=>
    array(4) {
      ["メニュー番号"]=> int(1)
      ["名前"]=> string(15) "チャーハン"
      ["値段"]=> string(6) "520円"
      ["作成日"]=> string(16) "2018-10-08"
    }
}

解説

とてもパラメータの数が多くて分かりにくいのですが、分解していくととても単純な構造です。

並び順を指定するパラメータ

  • SORT_ASC: アイテムを昇順でソートする
  • SORT_DESC: アイテムを降順でソートする

こちらのパラメータは省略が可能で、デフォルトでは昇順になっています。

適宜、省略したり指定したり調整をしていきます。

ソートするデータ型を指定するパラメータ

指定した要素をどのデータ型でソートするか指定します。

こちらも省略は可能ですが、(デフォルトはSORT_REGULAR)思わぬソート結果になる可能性があるので、きちんと指定しておきましょう。

パラメータ 説明
SORT_REGULAR 型を変更せずに比較します。
SORT_NUMERIC 数値として比較します。
SORT_STRING 文字列として比較します。
SORT_LOCALE_STRING 現在のロケールを考慮して、文字列として比較します。
ロケールはsetlocaleで変更可能です。
SORT_NATURAL nasort()と同様に自然順で文字列として比較します。
SORT_FLAG_CASE 大文字小文字を区別しない文字列のソートを指定します。
SORT_STRINGやSORT_NATURALなどと ‘|’ (ビット演算子)で組み合わせて指定します。

例文では’作成日’を数値型で降順で指定、’値段’を文字列型で昇順で指定しているわけですね。

指定した要素の順番でソートの優先順位は変わる

ソートする要素は2つだけではなく、3つでも4つでも指定可能です。

まさにmultisort(複数で並び替え)という名にふさわしい機能ですね!

ソートの優先順にパラメータを指定する必要があります。

例えば先程のパラメータの順番を

array_multisort($ArrPrice, SORT_ASC, SORT_STRING, $ArrDate, SORT_DESC, SORT_NUMERIC,
$ArrMenu);

のように’値段’と’作成日’の順番を入れ替えると、出力結果は以下のように変わります。

array(4) {
  [0]=>
    array(4) {
      ["メニュー番号"]=> int(1)
      ["名前"]=> string(15) "チャーハン"
      ["値段"]=> string(6) "520円"
      ["作成日"]=> string(16) "2018-10-08 18:00"
    }
  [1]=>
    array(4) {
      ["メニュー番号"]=> int(3)
      ["名前"]=> string(9) "天津飯"
      ["値段"]=> string(6) "650円"
      ["作成日"]=> string(16) "2020-02-24 11:00"
    }
  [2]=>
    array(4) {
      ["メニュー番号"]=> int(2)
      ["名前"]=> string(9) "中華飯"
      ["値段"]=> string(6) "650円"
      ["作成日"]=> string(16) "2019-12-15 12:00"
    }
  [3]=>
    array(4) {
      ["メニュー番号"]=> int(5)
      ["名前"]=> string(15) "マーボー丼"
      ["値段"]=> string(6) "700円"
      ["作成日"]=> string(16) "2019-12-15 12:00"
    }
}

優先順にそれぞれ
‘ソートしたい要素の値が入った配列’,  ‘並び順’,  ‘ソートする値の型’
のパラメータを指定する必要があります。

同じ値だった場合の並び順

ここで注意なのが、ソートしたい値が全く同じだった場合は元の配列のキー順が保持されるわけではないということです。
(どのようなロジックで並べられるのか、あまり分かってないようです)

もし同じ値になる可能性がある、順番が変わると支障がでるというケースでは、さらにソートの要素を増やして指定するのが安全かもしれませんね!