階層構造を持つ配列のマージ


環境

PHP7

やりたかったこと

階層のある配列どうしをマージしたかった。

$a = [
    'a' => 1,
    'b' => [
        'c' => 2,
        'd' => [
            'e' => 3,
            'f' => [
            ]
        ]
    ]
];

$b = [
    'b' => [
        'c' => 6,
        'd' => [
            'f' => [
                'g' => 4
            ]
        ]
    ]
];
function merge_function($a,$b)
{
    // なにがしかの処理
}
print_r(merge_function($a,$b));

期待される結果

php
Array
(
    [a] => 1
    [b] => Array
        (
            [c] => 6
            [d] => Array
                (
                    [e] => 3
                    [f] => Array
                        (
                            [g] => 4
                        )
                )
        )
)

↑要は、既にある要素は上書き、ない要素は追加を各階層ごとに行うって感じです。

過去記事もちょっと探してみたものの、それらしいことをやっている記事が見つかりませんでした。余り需要がないってことでしょうか。

やってみたこと

1)array_merge

function merge_function($a,$b)
{
    return array_merge($a,$b);
}
print_r(merge_function($a,$b));

結果↓

php
Array
(
    [a] => 1
    [b] => Array
        (
            [c] => 6
            [d] => Array
                (
                    [f] => Array
                        (
                            [g] => 4
                        )
                )
        )
)

第3階層がダメですね。

2)array_merge_recursive

function merge_function($a,$b)
{
    return array_merge_recursive($a,$b);
}
print_r(merge_function($a,$b));

結果↓

php
Array
(
    [a] => 1
    [b] => Array
        (
            [c] => Array
                (
                    [0] => 2
                    [1] => 6
                )

            [d] => Array
                (
                    [e] => 3
                    [f] => Array
                        (
                            [g] => 4
                        )
                )
        )
)

第2階層のcが配列に変化してしまってます。

3)配列結合演算子(+)

function merge_function($a,$b)
{
    return $a + $b;
}
print_r(merge_function($a,$b));

結果↓

php
Array
(
    [a] => 1
    [b] => Array
        (
            [c] => 2
            [d] => Array
                (
                    [e] => 3
                    [f] => Array
                        (
                        )
                )
        )
)

c、gが上書きできていません。

4)関数で自作

php
function merge_left(array $a,array $b)
{
    $merged_keys = [];
    foreach($a as $k => $v){
        if (isset($b[$k])){
            if (is_array($a[$k]) && is_array($b[$k])){
                $a[$k] = merge_left($a[$k],$b[$k]);
            }
            else{
                $a[$k] = $b[$k];
            }
            $merged_keys[] = $k;
        }
    }
    foreach($b as $k => $v){
        if (!in_array($k,$merged_keys,true)){
            $a[$k] = $b[$k];
        }
    }
    return $a;
}
function merge_function($a,$b)
{
    return merge_left($a,$b);
}
print_r(merge_function($a,$b));

結果↓

php
Array
(
    [a] => 1
    [b] => Array
        (
            [c] => 6
            [d] => Array
                (
                    [e] => 3
                    [f] => Array
                        (
                            [g] => 4
                        )
                )
        )
)

バグがありそうだけど、なんとなく良さそう!

思ったこと

  • do not trust PHP standard functions! Read PHP Manual carefully!
  • in_arrayは罠
  • もっといいやり方があったらコメントが欲しいです・・・(・ε・。)マジデ ありました!感謝!