PHP:如何合并两个数组并保持键名不重新索引?

304

我如何合并两个数组(一个带有字符串=>值对,另一个带有整数=>值对),同时保留字符串/整数键?因为它们中没有一个会重叠(因为一个只有字符串,另一个只有整数)。

这是我的当前代码(不起作用,因为array_merge正在重新索引具有整数键的数组):

// 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);

1
很奇怪:根据PHP文档页面,array_merge 不应该 这样做。字符串键实际上是整数的字符串表示吗? - owenmarshall
array_merge正在重新索引我的第二个数组。 即它将数组从array( 123 => "VALUE123" )更改为array( 0 => "VALUE123" ) - Garrett
嗯,那很有趣。我想PHP文档在这一点上可能有点不清楚。它说了如果所有数组都有数字键会发生什么,但它并没有明确说明如果它们没有数字键会发生什么。 - Steven Oxley
也许不是两年前的事了。但在2012年,文档对此一点非常明确。 - cartbeforehorse
实际上,文档 仍然不是非常清晰。 "Numeric" 实际上包括一个带有所有数字的字符串(PHP 5.3.3)。 assert(array(0=>0,1=>1) === array_merge(array('9'=>0), array('9'=>1))) - Bob Stein
6个回答

641
你可以简单地“添加”这些数组:
>> $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,
)

67
非常小心! + 运算符不是加法,它是一个联合运算。如果键不重叠,则一切都很好,但如果它们重叠...... - GordonM
115
如果有人想知道“如果它们确实重叠会怎样?”,php.net上的解释是:“+ 运算符返回右侧数组附加到左侧数组的结果;对于在两个数组中都存在的键,将使用左侧数组中的元素,并忽略右侧数组中匹配的元素。” - Flion
1
@DarioFumagalli 很棒!我无法想象你是如何得出这个结果的。我在自己的环境中无法复现它。我很想看看你用来生成这个结果的代码。我猜你肯定是在使用某些奇怪或者老旧版本的PHP。 - rineez
2
@DarioFumagalli 我不知道是我误解了还是自2016年以来它已经改变,但是此时此刻,操作print_r([2 => 56] + [2 => 30]);会产生Array([2] => 56)。因此,它保留了左侧指定的内容。这是PHP 7.1.19的情况,在https://repl.it上运行。 - Charles Wood

86

考虑到您已经

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

$merge = $replacement + $replaced;的执行结果如下:

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

从sum中得到的第一个数组将在最终输出中具有值。

执行$merge = $replaced + $replacement;将输出:

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

26
总之,当将两个数组相加时,第一个数组中的值会覆盖第二个数组中的值。 - Dziamid
1
我以为第二个会覆盖第一个。 :) - Asim K T
1
没错。这就是为什么我不能使用 $allValues += $newValues; - MarthyM
执行 $merge = $replacement + $replaced; 将输出:Array ( [4] => value2 [6] => value3 [1] => value1 ) - Chandan Sharma

54

我只想添加另一种在保留键的情况下进行合并的可能性。

除了使用“+”符号向现有数组添加键/值之外,您还可以使用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);

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

相同的键将被后面的数组覆盖。
还有一个array_replace_recursive函数,它也可以对子数组执行此操作。

在3v4l.org上查看实例


3

通过+运算符,可以轻松地将两个数组相加或合并,而不改变它们的原始索引。这在Laravel和CodeIgniter选择下拉菜单中非常有用。

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

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

  $arr_option = $empty_option + $option_list;

输出将为:

$arr_option = array(
   ''=>'Select Option'
   1=>'Red',
   2=>'White',
   3=>'Green',
 );

2

本文作者的要求是保留键值(keep keys)并且不覆盖(我认为是指 overwrite)。在一些情况下,如数值键,这是可能的,但如果是字符串键,则似乎不可能。

如果使用 array_merge(),则数字键将始终重新索引或重新编号。

如果使用 array_replace()array_replace_recursive(),它将从右到左进行重叠或覆盖。第一个数组上具有相同键的值将被第二个数组替换。

如果使用 $array1 + $array2,如评论中提到的,如果键相同,则将保留来自第一个数组的值,但放弃第二个数组。

自定义函数。

以下是我刚刚编写的函数,以满足相同的要求。您可以自由地将其用于任何目的。

/**
 * 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

测试。

<?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\'');

结果。

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
)

2
尝试使用array_replace_recursive或array_replace函数。最初的回答。
$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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接