32
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

配列のマージって色々ある

Last updated at Posted at 2014-12-18

PHPは色んな便利標準関数があって助かります。

そんな中でも、配列のマージ(合体)で希望通りの動きを実現するまでに
結構時間がかかってしまったのでメモ。

こちらも参考にしましたので、合わせてご覧ください。

前提

  • 多次元配列
  • キーは数字・文字混合で、変化させない(これが厄介でした)
  • 配列$beforeを元として、$artisanをマージする
  • キーが$beforeになく$artisanにあるものは単純に加える
  • キー$beforeにも$artisanにもあるものは、$artisanの値で上書きする
  • 結果は出来る限り$afterに($before$artisanを変える形になっても良し)

こんな配列を想定する

$before = array(
    '0' => 0,
    '1' => array(
        '0' => 0,
        '1' => 1,
    ),
    '2' => 2,
    'string' => 'string',
);

$artisan = array(
    '0' => '000',
    '1' => array(
        '0' => '000',
        '2' => '222',
    ),
    'string' => 'STRING',
    '3' => 3,
);

結果の想定

$after = array(
    '0' => '000',
    '1' => array(
        '0' => '000',
        '1' => 1,
        '2' => '222',
    ),
    '2' => 2,
    'string' => 'STRING',
    '3' => 3,
);

array_merge

$after = array(
    '0' => 0,
    '1' => array(
        '0' => 0,
        '1' => 1,
    ),
    '2' => 2,
    'string' => 'STRING',
    '3' => '000',
    '4' => array(
        '0' => '000',
        '2' => '222',
    ),
    '5' => 3,
);

キーが文字列の場合はきちんとマージされているが、
キーが数字の場合は添え字を振り直している。

array_merge_recursive

再帰的にマージする関数。

$after = array(
    '0' => 0,
    '1' => array(
        '0' => 0,
        '1' => 1,
    ),
    '2' => 2,
    'string' => array(
        '0' => 'string',
        '1' => 'STRING',
    ),
    '3' => '000',
    '4' => array(
        '0' => '000',
        '2' => '222',
    ),
    '5' => 3,
);

数字のキーは先ほどと変わらず、重複があった場合は振り直し。
文字のキーは重複があった場合、配列として全て保持するように変更されている。

+(プラス)演算子

$after = array(
    '0' => 0,
    '1' => array(
        '0' => 0,
        '1' => 1,
    ),
    '2' => 2,
    'string' => string',
    '3' => 3,
);

キーは文字数字問わず、重複する場合は+演算子の前にある配列が優先され、
子要素などを持っていようと、評価していない。
一次元目のキーだけを見て、重複していないもののみ追加している様子。

foreachで再帰的に回してみる

こちらより。

function mymerge($arr1, $arr2)
{
    foreach($arr2 as $key=>$value) {
        if(!is_array($arr1)) {
            $arr1 = array($arr1);
        }
        if(is_array($value)) {
            $arr1[$key] = mymerge($arr1[$key], $value);
        } else {
            $arr1[$key] = $value;
        }
    }
    return $arr1;
}
$after = array(
    '0' => '000',
    '1' => array(
        '0' => '000',
        '1' => 1,
        '2' => '222',
    ),
    '2' => 2,
    'string' => 'STRING',
    '3' => 3,
);

素晴らしい。要件通りです。

けれど、foreachで回すとなると大規模データでの速度が気になります。
(速度の検証はしませんが)他の手段はないものか探していたら…

array_replace_recursive

$after = array(
    '0' => '000',
    '1' => array(
        '0' => '000',
        '1' => 1,
        '2' => '222',
    ),
    '2' => 2,
    'string' => 'STRING',
    '3' => 3,
);

おお、まさかのreplace。盲点でしたが要件通りで完璧な挙動です。
同じく検証をしていないため、ひょっとしたらforeachと大差ないかもしれませんが、
標準関数なので信用しちゃいます。

追記

array_diffとかarray_intersectとかでも考えていたのですが、頭がパンクしたのでストップ。
これ以外にも上手いやり方がありましたらぜひご教示下さい。

32
34
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
32
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?