PHP: merge two arrays while keeping keys instead of reindexing?
Asked Answered
C

6

306

How can I merge two arrays (one with string => value pairs and another with int => value pairs) while keeping the string/int keys? None of them will ever overlap (because one has only strings and the other has only integers).

Here is my current code (which doesn't work, because array_merge is re-indexing the array with integer keys):

// get all id vars by combining the static and dynamic
$staticIdentifications = array(
 Users::userID => "USERID",
 Users::username => "USERNAME"
);
// get the dynamic vars, formatted: varID => varName
$companyVarIdentifications = CompanyVars::getIdentificationVarsFriendly($_SESSION['companyID']);
// merge the static and dynamic vars (*** BUT KEEP THE INT INDICES ***)
$idVars = array_merge($staticIdentifications, $companyVarIdentifications);
Cereal answered 20/7, 2010 at 16:12 Comment(5)
That's odd: according to the PHP doc page, array_merge shouldn't do that. Are the string keys actually string representations of integers?Adur
array_merge is reindexing my second array. ie. it is changing the array from array( 123 => "VALUE123" ) to array( 0 => "VALUE123" )Cereal
hmm, that is interesting. I suppose that the PHP documentation could be a little unclear on that point. It says what will happen if all of the arrays have numeric keys, but it doesn't specifically say what will happen if they don't.Tumbledown
maybe not 2 years ago. But in 2012, the documentation is Crystal clear on this point.Milinda
Actually, the documentation is still not crystal clear. "Numeric" actually includes a string with all digits (PHP 5.3.3). assert(array(0=>0,1=>1) === array_merge(array('9'=>0), array('9'=>1)))Suburb
C
647

You can simply 'add' the arrays:

>> $a = array(1, 2, 3);
array (
  0 => 1,
  1 => 2,
  2 => 3,
)
>> $b = array("a" => 1, "b" => 2, "c" => 3)
array (
  'a' => 1,
  'b' => 2,
  'c' => 3,
)
>> $a + $b
array (
  0 => 1,
  1 => 2,
  2 => 3,
  'a' => 1,
  'b' => 2,
  'c' => 3,
)
Clementineclementis answered 20/7, 2010 at 16:15 Comment(4)
Be VERY careful with this! The + operator is not an addition, it's a union. If the keys don't overlap then all is good, but if they do...Ulda
In case anyone wonders 'what if they DO overlap?' : php.net: "The + operator returns the right-hand array appended to the left-hand array; for keys that exist in both arrays, the elements from the left-hand array will be used, and the matching elements from the right-hand array will be ignored."Litigate
@DarioFumagalli Wonderful! I can't imagine how you got that result. I couldn't reproduce it in my environment. I would love to see the code you have used to produce that result. I guess you must be using some strange or old version of PHP.Flowers
@DarioFumagalli I don't know if I'm misunderstanding or if it's just changed since 2016, but at this point in time, the operation print_r([2 => 56] + [2 => 30]); yields Array ( [2] => 56 ). So, it's keeping the left hand side as specified. This is as of PHP 7.1.19, running on repl.it.Pillsbury
S
87

Considering that you have

$replaced = array('1' => 'value1', '4' => 'value4');
$replacement = array('4' => 'value2', '6' => 'value3');

Doing $merge = $replacement + $replaced; will output:

Array('4' => 'value2', '6' => 'value3', '1' => 'value1');

The first array from sum will have values in the final output.

Doing $merge = $replaced + $replacement; will output:

Array('1' => 'value1', '4' => 'value4', '6' => 'value3');
Stink answered 8/7, 2013 at 7:47 Comment(4)
To sum up, when adding 2 arrays, values from the first override values from the second.Nanette
I thought the second will override the first. :)Chastise
Exactly. That's why I couldn't use $allValues += $newValues;.Boonie
Doing $merge = $replacement + $replaced; will output: Array ( [4] => value2 [6] => value3 [1] => value1 )Foregut
K
55

I just want to add another possibility of doing a merge while keeping keys.

Besides adding key/values to existing arrays using the + sign you could do an array_replace.

$a = array(
    'foo'  => 'bar',
    'some' => 'string',
    'me'   => 'is original'
);
$b = array(
    42   => 'answer to the life and everything',
    1337 => 'leet',
    'me' => 'is overridden'
);

$merged = array_replace($a, $b);

The result will be:

$merged = array(
    'foo'  => 'bar',
    'some' => 'string',
    'me'   => 'is overridden',
    42     => 'answer to the life and everything',
    1337   => 'leet'
);

Same keys will be overwritten by the latter array.
There is also an array_replace_recursive, which do this for subarrays, too.

Live example on 3v4l.org

Karena answered 15/3, 2018 at 7:6 Comment(0)
H
3

Two arrays can be easily added or union without chaning their original indexing by + operator. This will be very help full in laravel and codeigniter select dropdown.

 $empty_option = array(
         ''=>'Select Option'
          );

 $option_list = array(
          1=>'Red',
          2=>'White',
          3=>'Green',
         );

  $arr_option = $empty_option + $option_list;

Output will be :

$arr_option = array(
   ''=>'Select Option'
   1=>'Red',
   2=>'White',
   3=>'Green',
 );
Hereinafter answered 30/10, 2018 at 4:57 Comment(0)
C
2

Try array_replace_recursive or array_replace functions

$a = array('userID' => 1, 'username'=> 2);
array (
  userID => 1,
  username => 2
)
$b = array('userID' => 1, 'companyID' => 3);
array (
  'userID' => 1,
  'companyID' => 3
)
$c = array_replace_recursive($a,$b);
array (
  userID => 1,
  username => 2,
  companyID => 3
)

http://php.net/manual/en/function.array-replace-recursive.php

Cowhide answered 2/2, 2019 at 16:39 Comment(0)
A
2

The OP.'s requirement is to preserve keys (keep keys) and not overlap (I think overwrite). In some cases such as numeric keys it is possible but if string keys it seems to be not possible.

If you use array_merge() the numeric keys will always re-index or renumbered.

If you use array_replace(), array_replace_recursive() it will be overlap or overwrite from the right to the left. The value with the same key on first array will be replaced with second array.

If you use $array1 + $array2 as the comment was mentioned, if the keys are same then it will keep the value from first array but drop the second array.

Custom function.

Here is my function that I just wrote to work on the same requirements. You are free to use for any purpose.

/**
 * Array custom merge. Preserve indexed array key (numbers) but overwrite string key (same as PHP's `array_merge()` function).
 * 
 * If the another array key is string, it will be overwrite the first array.<br>
 * If the another array key is integer, it will be add to first array depend on duplicated key or not. 
 * If it is not duplicate key with the first, the key will be preserve and add to the first array.
 * If it is duplicated then it will be re-index the number append to the first array.
 *
 * @param array $array1 The first array is main array.
 * @param array ...$arrays The another arrays to merge with the first.
 * @return array Return merged array.
 */
function arrayCustomMerge(array $array1, array ...$arrays): array
{
    foreach ($arrays as $additionalArray) {
        foreach ($additionalArray as $key => $item) {
            if (is_string($key)) {
                // if associative array.
                // item on the right will always overwrite on the left.
                $array1[$key] = $item;
            } elseif (is_int($key) && !array_key_exists($key, $array1)) {
                // if key is number. this should be indexed array.
                // and if array 1 is not already has this key.
                // add this array with the key preserved to array 1.
                $array1[$key] = $item;
            } else {
                // if anything else...
                // get all keys from array 1 (numbers only).
                $array1Keys = array_filter(array_keys($array1), 'is_int');
                // next key index = get max array key number + 1.
                $nextKeyIndex = (intval(max($array1Keys)) + 1);
                unset($array1Keys);
                // set array with the next key index.
                $array1[$nextKeyIndex] = $item;
                unset($nextKeyIndex);
            }
        }// endforeach; $additionalArray
        unset($item, $key);
    }// endforeach;
    unset($additionalArray);

    return $array1;
}// arrayCustomMerge

Testing.

<?php
$array1 = [
    'cat', 
    'bear', 
    'fruitred' => 'apple',
    3 => 'dog',
    null => 'null',
];
$array2 = [
    1 => 'polar bear',
    20 => 'monkey',
    'fruitred' => 'strawberry',
    'fruityellow' => 'banana',
    null => 'another null',
];

// require `arrayCustomMerge()` function here.

function printDebug($message)
{
    echo '<pre>';
    print_r($message);
    echo '</pre>' . PHP_EOL;
}

echo 'array1: <br>';
printDebug($array1);
echo 'array2: <br>';
printDebug($array2);


echo PHP_EOL . '<hr>' . PHP_EOL . PHP_EOL;


echo 'arrayCustomMerge:<br>';
$merged = arrayCustomMerge($array1, $array2);
printDebug($merged);


assert($merged[0] == 'cat', 'array key 0 should be \'cat\'');
assert($merged[1] == 'bear', 'array key 1 should be \'bear\'');
assert($merged['fruitred'] == 'strawberry', 'array key \'fruitred\' should be \'strawberry\'');
assert($merged[3] == 'dog', 'array key 3 should be \'dog\'');
assert(array_search('another null', $merged) !== false, '\'another null\' should be merged.');
assert(array_search('polar bear', $merged) !== false, '\'polar bear\' should be merged.');
assert($merged[20] == 'monkey', 'array key 20 should be \'monkey\'');
assert($merged['fruityellow'] == 'banana', 'array key \'fruityellow\' should be \'banana\'');

The results.

array1:

Array
(
    [0] => cat
    [1] => bear
    [fruitred] => apple
    [3] => dog
    [] => null
)

array2:

Array
(
    [1] => polar bear
    [20] => monkey
    [fruitred] => strawberry
    [fruityellow] => banana
    [] => another null
)

---
arrayCustomMerge:

Array
(
    [0] => cat
    [1] => bear
    [fruitred] => strawberry
    [3] => dog
    [] => another null
    [4] => polar bear
    [20] => monkey
    [fruityellow] => banana
)
Aubervilliers answered 19/1, 2021 at 19:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.