John Smith
John Smith

Reputation: 6207

php, array_merge_recursive works well with string keys only

$array1 = [
    '1' => '11',
    'b' => 1,
    3   => 33,
    8   => 8
];
$array2 = [
    '1' => '22',
    'b' => 2,
    3   => 44,
    9   => 9
];

$merged = array_merge_recursive($array1, $array2);

and the result is:

array(7) {
  [0]=>
  string(2) "11"
  ["b"]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    int(2)
  }
  [1]=>
  int(33)
  [2]=>
  int(8)
  [3]=>
  string(2) "22"
  [4]=>
  int(44)
  [5]=>
  int(9)
}

so lets take a glance: the only part is the 'b' keys, they are actually works. I dont want to overwrite anything of it but putting them together to an array. Thats good! But then keys the other numeric keys (int or string) are screwed up. I want to have this as result:

[
  '1' => ['11', '22']
  'b' => [1, 2]
  [3] => [33, 44]
  [8] => 8,
  [9] => 9
]

possible?

EDIT: of course keys "1" and 1 - string vs int key are the same

Upvotes: 3

Views: 2744

Answers (2)

mickmackusa
mickmackusa

Reputation: 47991

Can you rely on a native function to return your exact desired output? No. At least not in any version as of the date of this post (upto PHP8.1).

The good news is that crafting your own solution is very simple.

Code: (Demo)

foreach ($array2 as $key => $value) {
    if (!key_exists($key, $array1)) {
        $array1[$key] = $value;
    } else {
        $array1[$key] = (array)$array1[$key];
        $array1[$key][] = $value;
    }
}
var_export($array1);

I suppose I am less inclined to recommend this output structure because you have potentially different datatypes on a given level. If you were building subsequent code to iterate this data, you'd need to write conditions on every level to see if the data was iterable -- it just feels like you are setting yourself up for code bloat/convolution. I'd prefer a result which has consistent depth and datatypes.

Upvotes: 1

Stephan Vierkant
Stephan Vierkant

Reputation: 10164

Let's break down this question into to separate problems:

  1. When a key in the second array exist in the first array, you want to create an array and make the value the first element of that array.

To be honest, I don't know an easy way of solving this. I'm not sure there is one. And even if, I'm not sure you really want it. Such a function will lead to arrays having values that are a string or an array. How would you handle such an array?

Update: Well, there is one. Your example already shows that array_merge_recursive will convert values with a string key into an array. So 1 => 'foo' would be 0 => 'foo', but 'foo' => 'bar' will end up as 'foo' => ['bar']. I really don't understand this behaviour.

Using string keys would help you out in this case, but after learning more about array_merge_recursive I decided to avoid this function when possible. After I asked this question someone filed it as a bug in it since PHP 7.0, since it works differently in PHP 5.x.

  1. You want to keep the keys, while array_merge_resursive doesn't preserve integer keys, while it does for integer keys:

If the input arrays have the same string keys, then the values for these keys are merged together into an array, and this is done recursively, so that if one of the values is an array itself, the function will merge it with a corresponding entry in another array too. If, however, the arrays have the same numeric key, the later value will not overwrite the original value, but will be appended.

To make it worse, it handles differently when handling the nested arrays:

    $array1 = [30 => [500 => 'foo', 600 => 'bar']];
    $array2 = [];
    array_merge_recursive($array1, $array2);
    //[0 => [500=> 'foo', 600 => 'bar']];

So effectively, array_merge_resursive isn't really resursive.

Using array_replace_resursive solves that problem:

array_replace_recursive($array1, $array2);
//output: 
//array:5 [
//  1 => "22"
//  "b" => 2
//  3 => 44
//  8 => 8
//  9 => 9
//]

Please note that PHP is very consistent in being inconsistent. While array_merge_recursive isn't recursive, array_replace_recursive doesn't replace (it also appends):

If the key exists in the second array, and not the first, it will be created in the first array.

In many cases this is desired behavior and since naming functions isn't PHP's strongest point, you can consider this as a minor issue.

Upvotes: 7

Related Questions