我最近发现我的应用程序中有一个错误,原因是array_merge_recursive
的行为出乎意料。让我们看一下这个简单的例子:
$array1 = [
1 => [
1 => 100,
2 => 200,
],
2 => [
3 => 1000,
],
3 => [
1 => 500
]
];
$array2 = [
3 => [
1 => 500
]
];
array_merge_recursive($array1, $array2);
//returns: array:4 [ 0 => //...
我原本期望得到一个包含3个元素的数组:键为1、2和3。但是,这个函数返回了一个包含键为0、1、2和3的数组。因此,返回了4个元素,而我只期望了3个。当我将数字替换为它们的字母等价物(a、b、c)时,它返回了一个仅包含3个元素的数组:a、b和c。
$array1 = [
'a' => [
1 => 100,
2 => 200,
],
'b' => [
3 => 1000,
],
'c' => [
1 => 500
]
];
$array2 = [
'c' => [
1 => 500
]
];
array_merge_recursive($array1, $array2);
//returns: array:3 [ 'a' => //...
这对我来说是意外行为,但至少它已经被记录:
http://php.net/manual/zh/function.array-merge-recursive.php
如果输入数组有相同的字符串键名,则这些键名的值将合并成一个数组,并且这是递归完成的,因此,如果其中一个值本身就是一个数组,则该函数将把它与另一个数组中的相应条目合并。然而,如果这些数组具有相同的数字键名,则后面的值将不会覆盖原始值,而是附加到后面。
文档没有很清楚地说明“附加”是什么意思。事实证明,具有数字键的$array1
元素将被视为索引元素,因此它们将失去当前的键:返回的数组以0开始。当在数组中同时使用数字和字符串键时,这将导致奇怪的结果,但是如果你使用这种不良实践,请不要责怪PHP。在我的情况下,问题通过使用array_replace_recursive
得到解决,它完成了预期的操作。(在那个函数中,“replace”表示替换现有的值,否则附加;给函数命名真的很难!)
问题1:是否递归?
但问题并没有结束。我认为array_*_resursive
应该是一个递归函数:
递归是一种函数调用,其中函数调用自身。这样的函数也称为递归函数。结构递归是一种问题解决方法,其中问题的解决方案取决于相同问题的较小实例的解决方案。
事实证明它不是。虽然$array1
和$array2
都是关联数组,但上面示例中的$array1['c']
和$array2['c']
都是带有一个元素的索引数组:[1 => 500]
。让我们合并它们:
array_merge_recursive($array1['c'], $array2['c']);
//output: array:2 [0 => 500, 1 => 500]
这是预期输出,因为两个数组都有一个数字键(1
),所以第二个数组将附加到第一个数组末尾。新��组的第一个键为0。但让我们回到第一个示例:
array_merge_recursive($array1, $array2);
// output:
// array:3 [
// "a" => array:2 [
// 1 => 100
// 2 => 200
// ]
// "b" => array:1 [
// 3 => 1000
// ]
// "c" => array:2 [
// 1 => 500 //<-- why not 0 => 500?
// 2 => 500
// ]
//]
$array2['c'][1]
被附加到$array1['c']
中,但它的键是1和2。在之前的例子中,不是0和1。处理整数键时,主数组和其子数组的处理方式不同。
问题2:字符串键和整数键有很大的区别。
在编写这个问题时,我发现了另一件事情。当用字符串键替换子数组中的数字键时,情况变得更加混乱:
$array1 = [
'c' => [
'a' => 500
]
];
$array2 = [
'c' => [
'a' => 500
]
];
array_merge_recursive($array1, $array2);
// output:
// array:1 [
// "c" => array:1 [
// "a" => array:2 [
// 0 => 500
// 1 => 500
// ]
// ]
//]
使用字符串键会将
(int) 500
转换为 array(500)
,而使用整数键则不会。有人能解释一下这种行为吗?