我最近在我的应用程序中发现了一个由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/en/function.array-merge-recursive.php
如果输入数组具有相同的字符串键,则用于 这些键被合并在一起成为一个数组,这完成了 递归,因此,如果值之一是数组本身,则 函数将其与另一个数组中的对应条目合并 太。但是,如果数组具有相同的数字键,则后面的 值不会覆盖原始值,但会附加。
有关“附加”的含义的文档尚不清楚。事实证明,带有数字键的$array1
元素将被视为索引元素,因此它们将丢失当前键:返回的数组以0开头。这在同时使用数字和字符串时会导致奇怪的结果数组中的键,但是如果您使用这样的坏习惯,那就不要怪PHP。就我而言,该问题通过使用array_replace_recursive
来解决,它起到了预期的效果。 (该功能中的“替换”表示如果存在则替换,否则添加;命名功能很难!)
但这不是这个问题结束了。我以为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。处理整数键时,对主数组及其子数组的处理方式有所不同。
在撰写此问题时,我发现了其他问题。在子数组中用字符串键替换数字键时,变得更加混乱:
$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)
,而使用整数键则不会。
有人可以解释这种行为吗?
答案 0 :(得分:1)
如果我们退后一步,观察array_merge*()
函数仅在一个数组中的行为,那么我们可以一窥它如何以不同方式对待关联数组和索引数组:
$array1 = [
'k' => [
1 => 100,
2 => 200,
],
2 => [
3 => 1000,
],
'f' => 'gf',
3 => [
1 => 500
],
'99' => 'hi',
5 => 'g'
];
var_dump( array_merge_recursive( $array1 ) );
输出:
array(6) {
["k"]=>
array(2) {
[1]=>
int(100)
[2]=>
int(200)
}
[0]=>
array(1) {
[3]=>
int(1000)
}
["f"]=>
string(2) "gf"
[1]=>
array(1) {
[1]=>
int(500)
}
[2]=>
string(2) "hi"
[3]=>
string(1) "g"
}
如您所见,它使用了所有数字键,并忽略了它们的实际值,并按遇到的顺序将它们返回给您。我可以想象该函数是为了在底层C代码中保持完整性(或效率)而这样做的。
回到您的两个数组示例,它使用$array1
的值,对其进行排序,然后然后附加在$array2
后面。
这种行为是否理智是一个完全独立的讨论...
答案 1 :(得分:0)
您应该阅读提供的链接状态(强调我的意思):
如果输入数组具有相同的字符串键,则将这些键的值合并到一个数组中,这是递归完成的,因此值本身就是一个数组,该函数也会将其与另一个数组中的相应条目合并。 但是,如果数组具有相同的数字键,则更高的值不会覆盖原始值,但会添加。。 >