简体   繁体   中英

PHP Deep Extend Array

How can I do a deep extension of a multi dimensional associative array (for use with decoded JSON objects). I need the php equivalent of jQuery's $.extend(true, array1, array2) with arrays instead of JSON and in PHP.

Here's an example of what I need (array_merge_recursive didn't seem to do the same thing)

$array1 = ('1'=> ('a'=>'array1a', 'b'=>'array1b'));
$array2 = ('1'=> ('a'=>'array2a', 'c'=>'array2b'));

$array3 = array_extend($array1, $array2);

//$array3 = ('1'=> ('a'=>'array2a', 'b'=>'array1b', 'c'=>'array2b'))

Notice how array2 overrides array1 if it has same value (like how extension of classes works)

If you have PHP 5.3.0+, you can use array_replace_recursive which does exactly what you need:

array_replace_recursive() replaces the values of array1 with the same values from all the following arrays. If a key from the first array exists in the second array, its value will be replaced by the value from the second array. If the key exists in the second array, and not the first, it will be created in the first array. If a key only exists in the first array, it will be left as is. If several arrays are passed for replacement, they will be processed in order, the later array overwriting the previous values.

I use this in the same way I use angular.extend(dst, src) and jQuery.extend().

function extend($base = array(), $replacements = array()) {
    $base = ! is_array($base) ? array() : $base;
    $replacements = ! is_array($replacements) ? array() : $replacements;

    return array_replace_recursive($base, $replacements);
}

Example:

si() is a utility sanitize function that grabs $_POST or $_GET and returns an array.

        $s = extend(array(
            'page' => 1,
            'take' => 100,
            'completed' => 1,
            'incomplete' => 1,
        ), si());

Taken from array_merge docs:

function array_extend($a, $b) {
    foreach($b as $k=>$v) {
        if( is_array($v) ) {
            if( !isset($a[$k]) ) {
                $a[$k] = $v;
            } else {
                $a[$k] = array_extend($a[$k], $v);
            }
        } else {
            $a[$k] = $v;
        }
    }
    return $a;
}

This might be what you're looking for:

function array_extend(&$result) {
  if (!is_array($result)) {
    $result = array();
  }

  $args = func_get_args();

  for ($i = 1; $i < count($args); $i++) {
    // we only work on array parameters:
    if (!is_array($args[$i])) continue;

    // extend current result:
    foreach ($args[$i] as $k => $v) {
      if (!isset($result[$k])) {
        $result[$k] = $v;
      }
      else {
        if (is_array($result[$k]) && is_array($v)) {
          array_extend($result[$k], $v);
        }
        else {
          $result[$k] = $v;
        }
      }
    }
  }

  return $result;
}

Usage:

$arr1 = array('a' => 1, 'b' => 2, 'c' => 3);
$arr2 = array('b' => 'b', 'd' => 'd');
array_extend($arr1, $arr2);
print_r($arr1); // array('a' => 1, 'b' => 'b', 'c' => 3, 'd' => 'd')

// or, to create a new array and leave $arr1 unchanged use:
array_extend($arr3, $arr1, $arr2);
print_r($arr3); // array('a' => 1, 'b' => 'b', 'c' => 3, 'd' => 'd')

// or, use the return value:
print_r(array_extend($arr1, $arr2)); // but this will also modify $arr1

You should use: https://github.com/appcia/webwork/blob/master/lib/Appcia/Webwork/Storage/Config.php#L64

/**
 * Merge two arrays recursive
 *
 * Overwrite values with associative keys
 * Append values with integer keys
 *
 * @param array $arr1 First array
 * @param array $arr2 Second array
 *
 * @return array
 */
public static function merge(array $arr1, array $arr2)
{
    if (empty($arr1)) {
        return $arr2;
    } else if (empty($arr2)) {
        return $arr1;
    }

    foreach ($arr2 as $key => $value) {
        if (is_int($key)) {
            $arr1[] = $value;
        } elseif (is_array($arr2[$key])) {
            if (!isset($arr1[$key])) {
                $arr1[$key] = array();
            }

            if (is_int($key)) {
                $arr1[] = static::merge($arr1[$key], $value);
            } else {
                $arr1[$key] = static::merge($arr1[$key], $value);
            }
        } else {
            $arr1[$key] = $value;
        }
    }

    return $arr1;
}

With a little googling I found this:

/**
 * jquery style extend, merges arrays (without errors if the passed values are not arrays)
 *
 * @return array $extended
 **/
function extend() {
    $args = func_get_args();
    $extended = array();
    if(is_array($args) && count($args)) {
        foreach($args as $array) {
            if(is_array($array)) {
                $extended = array_merge($extended, $array);
            }
        }
    }
    return $extended;
}

extend($defaults, $new_options);

I guess here is the correct answer, because:

  • your answer have a bug with warning:

    Warning: Cannot use a scalar value as an array in...

Because $a is not always an array and you use $a[$k].

  • array_merge_recursive does indeed merge arrays, but it converts values with duplicate keys to arrays rather than overwriting the value in the first array with the duplicate value in the second array, as array_merge does.

  • other aswers are not recursives or not simple.

So, here is my answer: your answer without bugs:

function array_extend(array $a, array $b) {
    foreach($b as $k=>$v) {
        if( is_array($v) ) {
            if( !isset($a[$k]) ) {
                $a[$k] = $v;
            } else {
                if( !is_array($a[$k]){
                    $a[$k]=array();
                }
                $a[$k] = array_extend($a[$k], $v);
            }
        } else {
            $a[$k] = $v;
        }
    }
    return $a;
}

And my answer with ternary operator:

function array_extend(array $a, array $b){
    foreach($b as $k=>$v)
            $a[$k] = is_array($v)&&isset($a[$k])?
                array_extend(is_array($a[$k])?
                        $a[$k]:array(),$v):
                $v;
    return $a;
}

Edit: And a bonus one with as many arrays you want:

function array_extend(){
    $args = func_get_args();
    while($extended = array_shift($args))
            if(is_array($extended))
                    break;
    if(!is_array($extended))
            return FALSE;
    while($array = array_shift($args)){
        if(is_array($array))
                foreach($array as $k=>$v)
                        $extended[$k] = is_array($v)&&isset($extended[$k])?
                            array_extend(is_array($extended[$k])?
                                    $extended[$k]:array(),$v):
                            $v;
    }
    return $extended;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM