LoginSignup
6
2

More than 3 years have passed since last update.

簡単ですぐ使えるarray_multisortの複数並び替えの記述方法

Last updated at Posted at 2020-02-14

この記事は「データベース結果の連想配列をPHP側でソートしたい」ひと向けの内容になっています。
結論だけ知りたい方は「実際の記述例」を見てください。

読者対象

  • PHPv5.6以上
  • 連想配列内をソートしたい
  • 複数の指定がしたい(指定数は可変でやりたい)

まずはコード例から理解する

PHP array_mutli_sort

上記公式にある「データベースの結果をソートする」の例をベースにします。
(リンク先にとばなくても問題ありません)

データ構造

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から使うことができる可変長引数を使用しています。

PHP 関数の引数

あまり用途がない為、見慣れないかもしれません。ですがこの書き方が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_columnarray_multisortも速度的に問題なさそうですね。

array_multisortは今までなんとなくで使えていたので、理解できてよかった(・×・)ノ

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2