Skip to content

Instantly share code, notes, and snippets.

@ptz0n
Created January 20, 2012 08:43
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save ptz0n/1646171 to your computer and use it in GitHub Desktop.
Save ptz0n/1646171 to your computer and use it in GitHub Desktop.
Recursive merge of arrays with integer keys
<?php
/**
* Merge two dimensional arrays my way
*
* Will merge keys even if they are of type int
*
* @param array $array1 Initial array to merge.
* @param array ... Variable list of arrays to recursively merge.
*
* @author Erik Pettersson <mail@ptz0n.se>
*/
function array_merge_myway()
{
$output = array();
foreach(func_get_args() as $array) {
foreach($array as $key => $value) {
$output[$key] = isset($output[$key]) ?
array_merge($output[$key], $value) : $value;
}
}
return $output;
}
/**
* Version of array_merge_recursive without overwriting numeric keys
*
* @param array $array1 Initial array to merge.
* @param array ... Variable list of arrays to recursively merge.
*
* @link http://www.php.net/manual/en/function.array-merge-recursive.php#106985
* @author Martyniuk Vasyl <martyniuk.vasyl@gmail.com>
*/
function array_merge_recursive_new()
{
$arrays = func_get_args();
$base = array_shift($arrays);
foreach($arrays as $array) {
reset($base);
while(list($key, $value) = @each($array)) {
if(is_array($value) && @is_array($base[$key])) {
$base[$key] = array_merge_recursive_new($base[$key], $value);
}
else {
$base[$key] = $value;
}
}
}
return $base;
}
/**
* Marge arrays recursively and distinct
*
* Merges any number of arrays / parameters recursively, replacing
* entries with string keys with values from latter arrays.
* If the entry or the next value to be assigned is an array, then it
* automagically treats both arguments as an array.
* Numeric entries are appended, not replaced, but only if they are
* unique
*
* @param array $array1 Initial array to merge.
* @param array ... Variable list of arrays to recursively merge.
*
* @link http://www.php.net/manual/en/function.array-merge-recursive.php#96201
* @author Mark Roduner <mark.roduner@gmail.com>
*/
function array_merge_recursive_distinct()
{
$arrays = func_get_args();
$base = array_shift($arrays);
if(!is_array($base)) $base = empty($base) ? array() : array($base);
foreach($arrays as $append) {
if(!is_array($append)) $append = array($append);
foreach($append as $key => $value) {
if(!array_key_exists($key, $base) and !is_numeric($key)) {
$base[$key] = $append[$key];
continue;
}
if(is_array($value) or is_array($base[$key])) {
$base[$key] = array_merge_recursive_distinct($base[$key], $append[$key]);
}
else if(is_numeric($key))
{
if(!in_array($value, $base)) $base[] = $value;
}
else {
$base[$key] = $value;
}
}
}
return $base;
}
$first = array(
1327006800 => array('avgresponse' => 1803),
1327003200 => array('avgresponse' => 1453)
);
$second = array(
1327006800 => array('pageviews' => 123),
1327003200 => array('pageviews' => 345)
);
/**
* Test functions
*
*/
echo '<pre>';
// Recursive array merge will not work as expected
echo 'array_merge_recursive fails: ';
print_r(array_merge_recursive($first, $second));
// Let's try our function out
echo "\n", 'array_merge_myway success: ';
print_r(array_merge_myway($first, $second));
// Let's try another one
echo "\n", 'array_merge_recursive_new works too: ';
print_r(array_merge_recursive_new($first, $second));
// Let's try another one
echo "\n", 'array_merge_recursive_distinct also works: ';
print_r(array_merge_recursive_distinct($first, $second));
@ptz0n
Copy link
Author

ptz0n commented Jan 20, 2012

Output:

array_merge_recursive fails: Array
(
    [0] => Array
        (
            [avgresponse] => 1803
        )

    [1] => Array
        (
            [avgresponse] => 1453
        )

    [2] => Array
        (
            [pageviews] => 123
        )

    [3] => Array
        (
            [pageviews] => 345
        )

)

array_merge_myway success: Array
(
    [1327006800] => Array
        (
            [avgresponse] => 1803
            [pageviews] => 123
        )

    [1327003200] => Array
        (
            [avgresponse] => 1453
            [pageviews] => 345
        )

)

array_merge_recursive_new works too: Array
(
    [1327006800] => Array
        (
            [avgresponse] => 1803
            [pageviews] => 123
        )

    [1327003200] => Array
        (
            [avgresponse] => 1453
            [pageviews] => 345
        )

)

array_merge_recursive_distinct also works: Array
(
    [1327006800] => Array
        (
            [avgresponse] => 1803
            [pageviews] => 123
        )

    [1327003200] => Array
        (
            [avgresponse] => 1453
            [pageviews] => 345
        )

)

@karelv
Copy link

karelv commented Jan 9, 2014

Thanks.

I use array_merge_recursive_distinct

But I modified the line

            if(is_array($value) or is_array($base[$key])) {

into:

            if(is_array($value) or (array_key_exists($key, $base) and is_array($base[$key]))) {

As the original version thows an exception when the key does not exists yet...

@j1cs
Copy link

j1cs commented Oct 21, 2014

Perfect!!! Thank you very much!

@nestordgs
Copy link

Excellent! Thank for your this!

@jankosh
Copy link

jankosh commented Jun 2, 2018

Thank you very much, I couldn't figure this one on my own.
However after heavy usage I definitely recommend everyone to test the array_key_exists() against the bool data type with === (or !==) operator, rather than counting on !array_key_exists() - because when the searched key index is 0, then it also evaluates to false which can cause trouble.

@RedstoneFuture
Copy link

RedstoneFuture commented Mar 18, 2019

Great solution. But I need help for a small change to support the follow array-merge:

{"A":{"test":[]}} +
{"A":{"test":["test1"]}} +
{"A":{"test":["test2"]}}

=> for this result:

{"A":
  {"test":{
    "0":["test1"],
    "1":["test2"]
    }
  }
}

Can you help me? Its the same as your rekursiv merge function, but with your solution the ["test1"] will overrided by ["test2"], because he don't increment the key.

@fobiaxx
Copy link

fobiaxx commented Jan 2, 2023

If this function is required targeting PHP v8 (the each command is deprecated) you could do it this way:

function array_merge_recursive_new() {
  $arrays = func_get_args();
  $base = array_shift($arrays);
  foreach ($arrays as $array) {
    foreach (array_keys($array) as $i => $key) {
      $value = array_values($array)[$i];
      $base[$key] = is_array($value) && is_array($base[$key]) ? array_merge_recursive_new($base[$key], $value) : $value;
    }
  }
  return $base;
}

@afaaro
Copy link

afaaro commented Dec 6, 2023

Thank you so much, I was struggling with integer keys

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment