PHPを使っていると、配列の差分を取りたいという場面によく遭遇します。
通常の配列であれば、array_diff()を使って簡単に差分を抽出できるのですが、連想配列だとうまく行かないことが分かりました。そこで、array_udiffを使って、連想配列を抽出してみます。
目次
とりあえずコード
function associative_array_diff ($keys, $array_1, ...$array_n) { foreach ($array_n as $array_i) { $result = array_udiff($array_1, $array_i, function ($a, $b) use ($keys) { foreach ($keys as $key) { $cmp = spaceship_operator($a[$key], $b[$key]); //PHP7未満の場合 $cmp = $a[$key] <=> $b[$key]; //PHP7以上の場合 if ($cmp) return $cmp; } return $cmp; }); } } function spaceship_operator ($a, $b) { if ($a === $b) { return 0; } elseif ($a < $b) { return -1; } else { return 1; } }
連想配列を抽出する関数は、associative_array_diffです。こちらのQiitaの記事を参考にさせていただきました。ポイントは、array_udiffを使うという点です。
array_udiff
array_udiffは、データの比較にコールバック関数を用いて、配列の差を計算する関数です。(公式ドキュメントより)
ポイントになるのはコールバック関数です。返り値を以下のように設定することが義務付けられています。
- 最初の引数 < 2番目の引数 の場合:負の値
- 最初の引数 = 2番目の引数の場合:0
- 最初の引数 > 2番目の引数 : 正の値
このようなコールバック関数を用意することによって、連想配列の任意のキー(複数も可能)を比較して、差分を抽出することができるのです。
PHP7以降では、宇宙船演算子(<=>)によって、上記の3パターンを簡単に返すことが出来ます。一方でPHP7未満のバージョンだと、上のソースコードのspaceship_operatorのような関数を用意する必要があります。
使ってみるとこんな感じ
$arr_1 = [ 0 => [ 'id' => 1, 'name' => 'AAA', 'sex' => 'M', ], 1 => [ 'id' => 2, 'name' => 'BBB', 'sex' => 'M', ], 2 => [ 'id' => 3, 'name' => 'CCC', 'sex' => 'W', ], ]; $arr_2 = [ 0 => [ 'id' => 1, 'name' => 'DDD', 'sex' => 'M', ], 1 => [ 'id' => 2, 'name' => 'BBB', 'sex' => 'M', ], ]; associative_array_diff(['id', 'name'], $arr_1, $arr_2); // 出力:['id' => 3,'name' => 'CCC','sex' => 'W',]
今回は、idとnameの両方が一致したものは落とす(つまり、少なくともどちらかが違っていたら抽出する)ようにしました。
このように、連想配列であってもキーを指定して差分を抽出することが出来ました。