timetofly
timetofly

Reputation: 3077

How to merge two multidimensional arrays without overwriting existing keys in PHP?

I'm trying to add two multidimensional arrays. The function should be the exact same as adding the arrays together like so: $new_array = $array1 + $array2, except it should work with multidimensional arrays. This means that $array2 should not overwrite existing key values.

Example: let's say I have the following $default_array:

array
  'something' => int 0
  'nested' => 
    array (size=3)
      'a' => null
      'b' => null
      'c' => null

And the following $user_array:

array
  'something' => int 1000
  'nested' => 
    array (size=3)
      'a' => 1
  'new' => int 4

Adding the two together (like $user_array + $default_array) should give me:

array (size=4)
      'something' => int 1000
      'nested' => 
        array (size=3)
          'a' => 1
          'b' => null
          'c' => null
      'new' => int 4

Currently, it seems that if I add the two multidimensional arrays, the overwriting feature seems to only apply to the first array dimension. This means that in this case, 'nested' already exists in the $user_array, so it wouldn't append the other values.

EDIT: I should also mention that this would need to work with unlimited amount of dimensions.

EDIT 2: I just tried to apply Rain's answer, and it's not working as intended. First off, I'm getting "undefined index" notices. Secondly, the output it not as I wanted. Consider the following arrays:

        $default = array(
            'a' => array(
                'a' => 'a',
                'b' => 'b',
                'c' => 'c',
                ),
            );

        $user = array(
            'b' => 1,
            'a' => array(
                'b' => 'something',
                'c' => 'something else',
                'd' => 'd',
                ),
            'c' => 2);

The output I'm getting with Rain's function is this:

array (size=3)
  'a' => 
    array (size=3)
      'a' => string 'a' (length=1)
      'b' => string 'b' (length=1)
      'c' => string 'c' (length=1)
  'b' => int 1
  'c' => int 2

In this example, the 'b' and 'c' keys in the sub-array should be 'something' and 'something else', respectively. I'm not quite sure how to fix that.

Upvotes: 0

Views: 3147

Answers (3)

mickmackusa
mickmackusa

Reputation: 47864

I am amazes that no one offered you the native function array_replace_recursive() seven years ago. @bestprogrammerintheworld was on the right track but chose the wrong recursive call.

Using ANYTHING other than this function is going to be unnecessary code bloat and very likely to be worse performance.

Code: (Demo)

var_export(
    array_replace_recursive($default, $user)
);

Output:

array (
  'a' => 
  array (
    'a' => 'a',
    'b' => 'something',
    'c' => 'something else',
    'd' => 'd',
  ),
  'b' => 1,
  'c' => 2,
)

Upvotes: 1

Rain
Rain

Reputation: 363

A recursive function is definitely the way to go here. I just whipped up the following. Note the use of & in the function declaration of fill_in_aux. This causes the second argument (called $default) to be passed by reference, instead of by value. It is wrapped in fill_in so that the real "default" array is not modified.

function fill_in($user, $default) {
    fill_in_aux($user, $default);
    return $default;
}

function fill_in_aux($user, &$default) {
    foreach ($user as $key => $value) {
        if (is_array($value)) {
            fill_in_aux($user[$key], $default[$key]);
        } else {
            $default[$key] = $value;
        }
    }
}

This assumes that anything that is set in the $default array should be overwritten by anything that exists in the $user array, even if a key in the $user array is set to null. To change this behavior, add another if statement in the else clause in fill_in_aux.


Example Usage

With your example arrays, the output of

print_r(fill_in($user_array, $default_array));

is

Array
(
    [something] => 1000
    [nested] => Array
        (
            [a] => 1
            [b] =>
            [c] =>
        )

    [new] => 4
)

Breakdown

Normally, when calling a function in PHP, parameters are passed by value: the function gets a copy of the passed in variable. This means that the function can do whatever it wants to the parameters but when the function returns, the original variable that was passed is unaffected.

When an parameter is prefaced by an & symbol, the parameter is passed by reference, instead of being copied. When the function makes modifications to the parameter, it is actually making changes directly to the variable that was passed in.

For more information, check out: http://php.net/manual/en/language.references.pass.php

Upvotes: 1

bestprogrammerintheworld
bestprogrammerintheworld

Reputation: 5520

You could try using array_merge_recursive() (not just array_merge()) http://www.php.net/manual/en/function.array-merge-recursive.php

<?php
$defaultarray = array(
'something' => 0,
  'nested' => 
    array (
      'a' => null,
      'b' => null,
      'c' => null));

$newArray = array(
  'something' => 1000, 
  'nested' => 
    array ('a' => 1,
  'new' => 4));

$mergedArray = array_merge_recursive($defaultarray, $newArray);
echo print_r($mergedArray,true);
?>

Output: Array ( [something] => Array ( [0] => 0 [1] => 1000 ) [nested] => Array ( [a] => Array ( [0] => [1] => 1 ) [b] => [c] => [new] => 4 ) )

Upvotes: 2

Related Questions