🐘

PHPで配列をマージする方法まとめ【保存版】

2020/03/12に公開

2025/05/26 追記

PHP 7.4以降では 配列のアンパック を使って配列同士を結合する方法もあります。

配列のアンパックを使って2つの配列 $a$b を結合した新たな配列 $c を得るには、次のように書くことができます。

$c = [...$a, ...$b]

アンパックによる配列の結合は array_merge() による配列の結合と動作としては等価です。


PHPで配列をマージ(結合)する方法は3通りあります。

それぞれで微妙に挙動が違っていてとても紛らわしいので、可能な限り分かりやすく整理してみたいと思います。

方法ごとのポイント

+ による加算

  • 同じキーがある場合は 先勝ち (前の配列の値が残って、後ろの配列の値が無視される)
  • 「同じキー」と見なすのは、 数値キーと文字列キーの両方
  • 一次元めのキーしか見られず、 二次元め以降の配列の構造は変化しない

array_merge() による結合

  • 同じキーがある場合は 後勝ち (第一引数の配列の値が第二引数の配列の値で上書きされる)
  • 「同じキー」と見なすのは、 文字列キーのみ
  • 数値キーの部分はすべて別物として扱われ、新しい配列では 数値キーが0から始まる連番で振り直される
  • 一次元めのキーしか見られず、 二次元め以降の配列の構造は変化しない

array_merge_recursive() による結合

  • 同じキーがある場合は、新しい配列では そのキーの値が「それぞれの値を結合した配列」になる
  • 「同じキー」と見なすのは、 文字列キーのみ
  • 数値キーの部分はすべて別物として扱われ、新しい配列では 数値キーが0から始まる連番で振り直される
  • 二次元め以降の配列も、同じキーがあれば 再帰的に結合される
  • 同じキーの値を結合する際にも、これらのルールが適用される

とんでもなくややこしいですね…😓

具体例を見ながらもう少し詳しく確認してみましょう。

動作結果の例

例1(数値キーの振る舞い)

元の配列

$a = [
    'a0',
    'a1',
    'a2',
];

$b = [
    0 => 'b0',
    1 => 'b1',
    100 => 'b100',
];

結合後の配列

+ による加算では以下のようになります。

$a + $b === [
    0 => 'a0',
    1 => 'a1',
    2 => 'a2',
    100 => 'b100',
];
  • 同じ数値キーを持つ要素が上書きされている
  • 上書きされる際、 先勝ち$a の値が残っている
  • 異なる数値キーを持つ要素はそのまま残っている
  • 元の配列の数値キーがそのまま維持されている

また、地味ですが、この動作結果から「キーを明示しない配列で暗黙的に振られた数値キー」は「明示的に指定した数値キー」と完全に等価であることも分かりますね。

array_merge() による結合では以下のようになります。

array_merge($a, $b) === [
    0 => 'a0',
    1 => 'a1',
    2 => 'a2',
    3 => 'b0',
    4 => 'b1',
    5 => 'b100',
];
  • 同じ数値キーを持つ要素は別物扱い で、上書きされていない
  • 元の数値キーは失われて 0から始まる連番でキーが振り直されている

array_merge_recursive() による結合では以下のようになります。

array_merge_recursive($a, $b) === [
    0 => 'a0',
    1 => 'a1',
    2 => 'a2',
    3 => 'b0',
    4 => 'b1',
    5 => 'b100',
];
  • 同じ数値キーは別物扱いなので、 array_merge() とまったく同じ結果になっている

例2(文字列キーの振る舞い)

元の配列

$a = [
    'key0' => 'a0',
    'key1' => 'a1',
    'key2-a' => 'a2',
];

$b = [
    'key0' => 'b0',
    'key1' => 'b1',
    'key2-b' => 'b2',
];

結合後の配列

+ による加算では以下のようになります。

$a + $b === [
    'key0' => 'a0',
    'key1' => 'a1',
    'key2-a' => 'a2',
    'key2-b' => 'b2',
];
  • 同じキーを持つ要素が上書きされている
  • 上書きされる際、 先勝ち$a の値が残っている
  • 異なるキーを持つ要素はそのまま残っている

array_merge() による結合では以下のようになります。

array_merge($a, $b) === [
    'key0' => 'b0',
    'key1' => 'b1',
    'key2-a' => 'a2',
    'key2-b' => 'b2',
];
  • 同じキーを持つ要素が上書きされている
  • 上書きされる際、 後勝ち$b の値が残っている
  • 異なるキーを持つ要素はそのまま残っている

array_merge_recursive() による結合では以下のようになります。

array_merge_recursive($a, $b) === [
    'key0' => [
        0 => 'a0',
        1 => 'b0',
    ],
    'key1' => [
        0 => 'a1',
        1 => 'b1',
    ],
    'key2-a' => 'a2',
    'key2-b' => 'b2',
];
  • 同じキーを持つ要素が 上書きではなく結合されている
  • 異なるキーを持つ要素はそのまま残っている

例3(二次元配列の振る舞い)

元の配列

$a = [
    'key' => [
        'a0',
        'a1',
    ],
];

$b = [
    'key' => [
        'b0',
        'b1',
    ],
];

結合後の配列

+ による加算では以下のようになります。

$a + $b === [
    'key' => [
        0 => 'a0',
        1 => 'a1',
    ],
];
  • 一次元めで同じキーを持つ要素が上書きされている
  • 上書きされる際、 先勝ち$a の値が残っている
  • 二次元めの配列構造は「勝ったほうの配列」のまま何も変化していない

array_merge() による結合では以下のようになります。

array_merge($a, $b) === [
    'key' => [
        0 => 'b0',
        1 => 'b1',
    ],
];
  • 一次元めで同じキーを持つ要素が上書きされている
  • 上書きされる際、 後勝ち$b の値が残っている
  • 二次元めの配列構造は「勝ったほうの配列」のまま何も変化していない

array_merge_recursive() による結合では以下のようになります。

array_merge_recursive($a, $b) === [
    'key' => [
        0 => 'a0',
        1 => 'a1',
        2 => 'b0',
        3 => 'b1',
    ],
];
  • 一次元めで同じキーを持つ要素が 上書きではなく結合されている
  • 二次元めの配列構造において、 数値キーは0から始まる連番で振り直されている

要するに何に気をつければいいのか

方法ごとの詳細な振る舞いを確認してきましたが、要するに何に気をつければいいかと言うと、

  • array_merge()array_merge_recursive() では数値キーをリセットされてしまうので、 数値キーに意味を持たせている配列の結合には使えない
  • 代わりに + による加算を使う場合、 「先勝ち」「後勝ち」が逆なので気を付ける
  • 数値キーをリセットせずに再帰的に結合したい場合は、自力でループを書くしかない

というところだと思います。

数値キーに意味を持たせている配列というのは、例えば id をキーにしたエンティティの配列などがよくあるパターンでしょう。

まとめ

  • PHPで配列をマージ(結合)する方法は、 + による加算、 array_merge()array_merge_recursive() の3通り
  • それぞれで 「先勝ち」「後勝ち」 が違っていたり、 数値キーがリセットされる/されない が違っていたりするので気を付ける
  • 特に「数値キーに意味を持たせている配列」をマージする際に注意が必要
GitHubで編集を提案

Discussion