Skip to content

Instantly share code, notes, and snippets.

@markvds
Last active December 1, 2022 10:38
Show Gist options
  • Save markvds/249e627281ac0b1831ec06204a17e0e3 to your computer and use it in GitHub Desktop.
Save markvds/249e627281ac0b1831ec06204a17e0e3 to your computer and use it in GitHub Desktop.
Alternate Merge

Alternate merging for arrays in php

The problem

Given the folowing arrays:

$a1 = [
    'name' => 'First Name',
    'address' => [
        'street' => 'Street 123',
        'postcode' => '1234AB',
        'city' => 'Amsterdam',
    ],
    'kids' => [
        0 => [
            'name' => 'Kid 1',
        ],
        1 => [
            'name' => 'Kid 2',
        ],
    ],
    'nationality' => 'NL',
];

$a2 = [
    'name' => 'Second Name',
    'address' => [
        'street' => 'Street 456',
        'postcode' => '1234AZ',
    ],
    'kids' => [
        0 => [
            'name' => 'Kid 1',
        ],
        1 => [
            'name' => 'Kid 3',
        ],
    ],
];

When using array_merge(), items on the highest level will always be overwritten:

$a3 = array_merge($a1, $a2);

// Is the same as
$a3 = [
    'name' => 'Second Name',
    'address' => [
        'street' => 'Street 456',
        'postcode' => '1234AZ',
    ],
    'kids' => [
        0 => [
            'name' => 'Kid 1',
        ],
        1 => [
            'name' => 'Kid 3',
        ],
    ],
    'nationality' => 'NL',
]

Then there is array_merge_recursive(), which will turn scalars into arrays when merging:

$a3 = array_merge_recursive($a1, $a2);

// Is the same as
$a3 = [
    'name' => [
        0 => 'First Name',
        1 => 'Second Name',
    ],
    'address' => [
        'street' => [
            0 => 'Street 123',
            1 => 'Street 456',
        ],
        'postcode' => [
            0 => '1234AB',
            1 => '1234AZ',
        ],
        'city' => 'Amsterdam',
    ],
    'kids' => [
        0 => [
            'name' => 'Kid 1',
        ],
        1 => [
            'name' => 'Kid 2',
        ],
        2 => [
            'name' => 'Kid 1',
        ],
        3 => [
            'name' => 'Kid 3',
        ],
    ],
    'nationality' => 'NL',
];

The solution

The code in this gist will overwrite scalar values, will (uniquely) add indexed arrays and will merge associative arrays.

$a3 = arrayMergeAlternate($a1, $a2);

// Is the same as
$a3 = [
    'name' => 'Second Name',
    'address' => [
        'street' => 'Street 456',
        'postcode' => '1234AZ',
        'city' => 'Amsterdam',
    ],
    'kids' => [
        0 => [
            'name' => 'Kid 1',
        ],
        1 => [
            'name' => 'Kid 2',
        ],
        2 => [
            'name' => 'Kid 3',
        ],
    ],
    'nationality' => 'NL',
];
<?php
/**
* Recursively merge two config arrays with a specific behavior:
*
* 1. scalar values are overridden
* 2. array values are extended uniquely if all keys are numeric
* 3. all other array values are merged
*
* @param array $original
* @param array $override
* @return array
* @see http://stackoverflow.com/a/36366886/6812729
*/
function arrayMergeAlternate(array $original, array $override): array
{
foreach ($override as $key => $value) {
if (isset($original[$key])) {
if (!is_array($original[$key])) {
if (is_numeric($key)) {
// Append scalar value
$original[] = $value;
} else {
// Override scalar value
$original[$key] = $value;
}
} elseif (array_keys($original[$key]) === range(0, count($original[$key]) - 1)) {
// Uniquely append to array with numeric keys
$original[$key] = array_values(array_unique(array_merge($original[$key], $value), SORT_REGULAR));
} else {
// Merge all other arrays
$original[$key] = arrayMergeAlternate($original[$key], $value);
}
} else {
// Simply add new key/value
$original[$key] = $value;
}
}
return $original;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment