Monday, September 20, 2010

array_merge() vs array concatenation and array_merge_recursive_overwrite()

I never seem to remember all the differences and aspects of the array_merge() function and array concatenation (not even documented thoroughly). On top there is this array_merge_recursive() which behaves like array_merge() though.

To start with I’ll say that array() + array() (called array concatenation) is in general NOT the same as array_merge(). It may behave in the same way if your arrays have all keys different. In this case you get both function and operator return the same result:


Next step: the function and the operator differ in the way they process numeric indexes as opposed to string keys.
For numeric indexes:
·         operator + is appending new numeric indexes to the end of array 1, but keeps the newly added indexes intact.
·         array_merge() is appending second array values and reindexing all indexes starting from 0.
·         both will NOT overwrite the values in array 1 with values of array 2.
For string indexes:
·         operator + is NOT overwriting the values
·         array_merge() is overwriting.
·         both will not reindex string keys (this makes no sense, really).
·         array_merge_recursive() is doing something unprecedented and undocumented though. For a value with the same key it’s making a sub-array with both values combined. 
That’s why people create such an amount of custom merge functions like
·         array_merge_recursive_distinct
·         array_merge_recursive_leftsource
·         array_merge_recursive_unique
·         array_merge_recursive_keys
·         array_merge_recursive_keep_keys
·         array_merge_recursive2
·         array_merge_replace
·         array_merge_n
·         array_merge_2
·         array_merge_replace_recursive
all of which are described in the comments to http://www.php.net/manual/en/function.array-merge-recursive.php

As you see the choice of the way to combine arrays is not so trivial. I’ve tried to summarize the data into the following table.

Operation/Function
Source Data
Overwrite?
Reindex?
Recursive?
Compares
KeysOrder?
array() + array()
Numeric
NO
No
Yes
Keys!
important
array() + array()
Associative
NO
No
Yes
Keys!
important
array_merge(a, a)
Numeric
NO!!!
YES
No, ?
Values!

array_merge(a, a)
Associative
Yes
NO
No, ?
Values!

array_merge_recursive
Numeric


Yes


array_merge_recursive
Associative


Yes



This was much to hard for me to remember so I’ve made a function which serves most of my purposes and behaves consistently and conveniently:
·         it preserves keys both indexes and string keys (no-reindexing)
·         it overwrites values from array1 with values of array2 provided they have the same keys
·         it works recursively as much as it can, but doesn’t create sub-arrays when source values are not arrays
·         pretty simple and straightforward it is (probably too simple to be built-in into PHP).
Enjoy:

function array_merge_recursive_overwrite($ar1, $ar2) {
       if (!is_array($ar1)) {
              $ar1 = $ar2;
       } else if (!is_array($ar2)) {
              //return $ar1;
       } else {
              foreach ($ar2 as $key2 => $val2) {
                     $subindex = isset($ar1[$key2]) ? $ar1[$key2] : NULL;
                     $ar1[$key2] = array2::array_merge_recursive_overwrite($subindex, $val2);
              }
       }
       return $ar1;
}

No comments: