【PHP】array_udiffで連想配列の差分を抽出する方法




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は、データの比較にコールバック関数を用いて、配列の差を計算する関数です。(公式ドキュメントより)

array_udiff関数
array_udiff(最初の配列, 2番目の配列, […n番目の配列], コールバック関数);

ポイントになるのはコールバック関数です。返り値を以下のように設定することが義務付けられています。

  • 最初の引数 < 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の両方が一致したものは落とす(つまり、少なくともどちらかが違っていたら抽出する)ようにしました。

このように、連想配列であってもキーを指定して差分を抽出することが出来ました。