この記事は「データベース結果の連想配列をPHP側でソートしたい」ひと向けの内容になっています。
結論だけ知りたい方は「実際の記述例」を見てください。
読者対象
- PHPv5.6以上
- 連想配列内をソートしたい
- 複数の指定がしたい(指定数は可変でやりたい)
まずはコード例から理解する
上記公式にある「データベースの結果をソートする」の例をベースにします。
(リンク先にとばなくても問題ありません)
データ構造
id | code | name |
---|---|---|
1 | E6B722 | きいろ |
2 | 74367D | むらさきいろ |
3 | FF0000 | あかいろ |
4 | DF3C94 | ももいろ |
データを可視化(デバッグ時の表示構造)
array (
0 => array(
'id' => 1,
'code' => 'E6B722',
'name' => 'きいろ',
),
1 => array(
'id' => 2,
'code' => '74367D',
'name' => 'むらさきいろ',
),
2 => array(
'id' => 3,
'code' => 'FF0000',
'name' => 'あかいろ',
),
3 => array(
'id' => 4,
'code' => 'DF3C94',
'name' => 'ももいろ',
)
);
また、$sortList
という配列にはあらかじめソート順を指定したデータを入れておきます。
$sortList[] = array('key' => 'id', 'sort' => SORT_ASC);
$sortList[] = array('key' => 'code', 'sort' => SORT_DESC);
今回は2つのソートを指定しました。
SQLに例えると、下記のようなイメージです。
ORDER BY id ASC, code DESC;
実際の記述例
// DBのデータを$dataに代入しているというイメージで読んでください
$data = $model->get()->toArray();
$result = $this->getSortArray($data, $sortList);
private function getSortArray($data, $sortList)
{
$argument = [];
if (empty($sortList)) {
return $data;
}
foreach ((array)$sortList as $list) {
$argument[] = array_column($data, $list['key']);
$argument[] = $list['sort'];
}
$argument[] = $data;
array_multisort(...$argument);
return end($argument);
}
カラムがもっと多く、更に順序も2つではなく4つ指定したい!という人は$sortList
に条件を追加してあげれば解決します。
結果だけ知りたい、という方は上記の処理内容で終わりになります。
以降は処理についての簡単な解説をしていきます。
array_multisortについて
まずは冗長になりますが、array_multisortの理解からです。
array_multisortは複数の配列や多次元配列をソートしてくれます。
そして公式サイトを見たことがある方は、引数の多さに混乱したかもしれません。
なのでまずは整理をしてみました。
array_multisort(
引数1: 並び替えしたい配列を指定 | 必須
引数2: SORT_ASCかSORT_DESCを指定 | 省略可
引数3: ソート時のデータ形式を指定 | 省略可
...
(以下、上記と同じ指定方法)
)
このように、実は意味合いが違う3つの引数指定があるだけです。
4つ目の引数からは、また追加の配列を指定し、ソート順を指定して…という風になるだけです。大切なのは3つです。
つまり、シンプルに考えるとただの配列もソートができるわけですね。
そして究極を言えば、下記でも動きます。
$array = ['2', '1', '3'];
array_multisort($array);
// array(0 => '1', 1 => '2', 2 => '3')
引数2を省略した場合は昇順になると公式に書いています。
なのでシンプルに昇順に並び替えたいなら、上記のような指定でおわりです。
多次元のソートは引数4以降の仕様を理解する
さて、ここまでの理解が得られたなら、次は引数4で指定ができる追加配列についての仕様を理解していきます。
追加配列は、それ以前のソートを引き継ぐことができます。
$array = ['2', '1', '3'];
$array2 = ['1', '2', '3'];
array_multisort($array, $array2);
// array(0 => '1', 1 => '2', 2 => '3')
// array2(0 => '2', 1 => '1', 2 => '3')
上記は引数2と引数3を省略し「ソートしたい配列」を2つ指定しています。
結果を見てもらうと分かる通り、$array2
は$array
と同じ順番でソートされています。つまりソートを引き継いでいます。
この特性を応用すると、連想配列の複雑なソートを実現することができます。
上記処理の解説
ではようやく今回のプログラムの解説に移ります。
$data = $model->get()->toArray();
$result = $this->getSortArray($data, $sortList);
private function getSortArray($data, $sortList)
{
// ※1
$argument = [];
if (empty($sortList)) {
return $data;
}
// ※2
foreach ((array)$sortList as $list) {
$argument[] = array_column($data, $list['key']);
$argument[] = $list['sort'];
}
// ※3
$argument[] = $data;
// ※4
array_multisort(...$argument);
return end($argument);
}
※1について
$argument = [];
可変の引数をこの配列に格納するため宣言しています。
※2について
$sortList
の定義をおさらいします。
$sortList[] = array('key' => 'id', 'sort' => SORT_ASC);
$sortList[] = array('key' => 'code', 'sort' => SORT_DESC);
key
は、「連想配列内のどのプロパティ名でソートするのか」を意味します。
またsort
はarray_multisortの引数2に該当します。
foreach ((array)$sortList as $list) {
// 引数1にソートしたい配列を指定
$argument[] = array_column($data, $list['key']);
// 引数2に並び順を指定( 昇順or降順 )
$argument[] = $list['sort'];
// 引数3は省略
}
引数1と引数2のループが上記でされます。
引数2や引数3は配列ではないので、そこに配列が指定されると自動的に引数1である「ソートしたい配列」として認識してくれます。
ちなみに、
$argument[] = array_column($data, $list['key']);
はPHPv5.5以降で使える構文で、
foreach ((array)$data as $row) {
// ex) $row: ['id' => 1, 'code' => 'E6B722', 'name' => 'きいろ']
$argument[] = $row[$list['key']];
}
上記のforeachと同じ結果を返してくれます。2重ループの見た目にならなくてスッキリします。
※3について
$argument[] = $data;
本当にソートしたい配列を1番最後に指定します。
この配列は、それ以前の引数で指定された配列の順序をすべて引き継いだソートが適用されます。
※4について
array_multisort(...$argument);
PHPのv5.6から使うことができる可変長引数を使用しています。
あまり用途がない為、見慣れないかもしれません。ですがこの書き方がarray_multisortと相性が良く、スマートな処理になりました。
アンチパターン
ちなみに、やらかしたパターンを紹介します…。
$data = $model->get()->toArray();
$result = $this->getSortArray($data, $sortList);
private function getSortArray($data, $sortList)
{
foreach ((array)$sortList as $list) {
$array[] = array_column($data, $list['key']);
// 1回目にidでソート、2回目にcodeでソート
array_multisort($array, $list['sort'], $data);
}
return $data;
}
上記の場合は、id
でソートした結果の$data
に、更にcode
でソートをしています。ですが上記は意図したとおりに動きません。
SQLで例えると、
ORDER BY id ASC, code DESC
このような結果を期待しているのですが、
ORDER BY code DESC
最後のソートだけが適用されます。
もし良さげでしたら、ぜひ可変長引数を使ってみてください。
最後に
ざっと調べた感じ、array_column
もarray_multisort
も速度的に問題なさそうですね。
array_multisort
は今までなんとなくで使えていたので、理解できてよかった(・×・)ノ