Skip to content

Commit 1d6ada9

Browse files
committed
优化Arr::mergeDeep
1 parent 3310a3b commit 1d6ada9

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

src/helper/Arr.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,7 @@ public static function has($array, $keys)
356356
*/
357357
public static function isAssoc(array $array)
358358
{
359-
$keys = array_keys($array);
360-
361-
return array_keys($keys) !== $keys;
359+
return !array_is_list($array);
362360
}
363361

364362
/**
@@ -632,16 +630,30 @@ public static function wrap($value)
632630
return is_array($value) ? $value : [$value];
633631
}
634632

633+
/**
634+
* Recursively merge arrays.
635+
* If the value is an associative array, it will be merged recursively.
636+
* If the value is an indexed array, it will be replaced entirely.
637+
*
638+
* @param array ...$arrays
639+
* @return array
640+
*/
635641
public static function mergeDeep(array ...$arrays): array
636642
{
637643
$result = [];
638644
foreach ($arrays as $array) {
639645
foreach ($array as $key => $value) {
640646
if (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
641-
$result[$key] = self::mergeDeep(
642-
$result[$key],
643-
$value
644-
);
647+
// 只有当两个数组都是关联数组时才递归合并
648+
if (self::isAssoc($result[$key]) && self::isAssoc($value)) {
649+
$result[$key] = self::mergeDeep(
650+
$result[$key],
651+
$value
652+
);
653+
} else {
654+
// 如果任一数组是索引数组,则直接覆盖
655+
$result[$key] = $value;
656+
}
645657
} else {
646658
$result[$key] = $value;
647659
}

tests/IsAssocTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use think\helper\Arr;
6+
7+
class IsAssocTest extends TestCase
8+
{
9+
public function testEmptyArray()
10+
{
11+
// 空数组不是关联数组
12+
$this->assertFalse(Arr::isAssoc([]));
13+
}
14+
15+
public function testSequentialArray()
16+
{
17+
// 顺序索引数组不是关联数组
18+
$this->assertFalse(Arr::isAssoc([1, 2, 3]));
19+
$this->assertFalse(Arr::isAssoc(['a', 'b', 'c']));
20+
$this->assertFalse(Arr::isAssoc([null, false, true]));
21+
}
22+
23+
public function testNonSequentialArray()
24+
{
25+
// 非顺序索引数组是关联数组
26+
$this->assertTrue(Arr::isAssoc([1 => 'a', 0 => 'b'])); // 键顺序不是0,1
27+
$this->assertTrue(Arr::isAssoc([1 => 'a', 2 => 'b'])); // 不是从0开始
28+
$this->assertTrue(Arr::isAssoc([0 => 'a', 2 => 'b'])); // 不连续
29+
}
30+
31+
public function testStringKeys()
32+
{
33+
// 字符串键的数组是关联数组
34+
$this->assertTrue(Arr::isAssoc(['a' => 1, 'b' => 2]));
35+
// 注意:PHP会将字符串数字键'0'、'1'自动转换为整数键0、1
36+
// 所以这个实际上是顺序索引数组,不是关联数组
37+
$this->assertFalse(Arr::isAssoc(['0' => 'a', '1' => 'b']));
38+
$this->assertTrue(Arr::isAssoc(['a' => 'a', 0 => 'b'])); // 混合键
39+
}
40+
41+
public function testMixedKeys()
42+
{
43+
// 混合键类型的数组是关联数组
44+
$this->assertTrue(Arr::isAssoc([0 => 'a', 'b' => 'b']));
45+
$this->assertTrue(Arr::isAssoc(['a' => 1, 2 => 'b']));
46+
}
47+
48+
}

tests/MergeDeepTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use think\helper\Arr;
6+
7+
class MergeDeepTest extends TestCase
8+
{
9+
public function testMergeDeepWithAssociativeArrays()
10+
{
11+
// 测试关联数组的递归合并
12+
$array1 = ['a' => ['b' => 2], 'c' => 3];
13+
$array2 = ['a' => ['b' => 4, 'd' => 5], 'e' => 6];
14+
15+
$result = Arr::mergeDeep($array1, $array2);
16+
17+
$expected = [
18+
'a' => ['b' => 4, 'd' => 5],
19+
'c' => 3,
20+
'e' => 6
21+
];
22+
23+
$this->assertEquals($expected, $result);
24+
}
25+
26+
public function testMergeDeepWithIndexedArrays()
27+
{
28+
// 测试索引数组的覆盖
29+
$array1 = ['a' => [1, 2, 3], 'b' => 2];
30+
$array2 = ['a' => [4, 5], 'c' => 3];
31+
32+
$result = Arr::mergeDeep($array1, $array2);
33+
34+
$expected = [
35+
'a' => [4, 5], // 索引数组应该被完全覆盖
36+
'b' => 2,
37+
'c' => 3
38+
];
39+
40+
$this->assertEquals($expected, $result);
41+
}
42+
43+
public function testMergeDeepWithMixedArrays()
44+
{
45+
// 测试混合数组类型
46+
$array1 = [
47+
'a' => ['b' => 2, 'c' => 3],
48+
'd' => [1, 2, 3],
49+
'e' => 4
50+
];
51+
52+
$array2 = [
53+
'a' => ['b' => 5, 'f' => 6],
54+
'd' => [7, 8],
55+
'g' => 9
56+
];
57+
58+
$result = Arr::mergeDeep($array1, $array2);
59+
60+
$expected = [
61+
'a' => ['b' => 5, 'c' => 3, 'f' => 6], // 关联数组递归合并
62+
'd' => [7, 8], // 索引数组被覆盖
63+
'e' => 4,
64+
'g' => 9
65+
];
66+
67+
$this->assertEquals($expected, $result);
68+
}
69+
}

0 commit comments

Comments
 (0)