PHP中合并相同键值的数组是常见的数据处理需求,如整合表单提交或配置项,可通过array_merge()实现基础合并,键名冲突时后值覆盖前值;若需保留所有值,可遍历数组,用isset()判断键是否存在,存在则将值合并为数组(如$arr[$key][] = $value),array_replace()支持递归合并,适用于多维数组场景,根据业务需求选择合适方法,可高效解决键值重复问题。
PHP中相同键值的合并方法与技巧
在PHP开发中,数组是最常用的数据结构之一,而“相同键值的合并”是一个高频需求——无论是统计多个数据源的汇总值、合并配置项,还是处理重复记录的聚合,都需要对数组中键名相同的元素进行合并处理,本文将详细介绍PHP中实现相同键值合并的多种方法,涵盖一维数组、多维数组等场景,并对比不同方法的适用场景与注意事项。
一维数组的相同键值合并:累加与覆盖
需求场景
假设有两个数组,它们的键名可能存在重复,需要合并后的数组根据需求实现“值累加”或“值覆盖”。
$array1 = ['a' => 1, 'b' => 2, 'c' => 3]; $array2 = ['a' => 4, 'b' => 5, 'd' => 6];
- 累加合并:期望结果
['a' => 5, 'b' => 7, 'c' => 3, 'd' => 6](相同键的值相加); - 覆盖合并:期望结果
['a' => 4, 'b' => 5, 'c' => 3, 'd' => 6](后一个数组的值覆盖前一个)。
覆盖合并:array_merge() 与 运算符
(1)array_merge() 函数
array_merge() 用于合并一个或多个数组,当键名重复时,后一个数组的值会覆盖前一个,键名保持不变。
$result = array_merge($array1, $array2); print_r($result); // 输出:Array ( [a] => 4 [b] => 5 [c] => 3 [d] => 6 )
注意:如果数组是数字索引(如 [0 => 'a', 1 => 'b']),array_merge() 会重新索引数字键,而字符串键不受影响。
(2) 运算符(数组 union)
运算符用于数组的“联合”(union),当键名重复时,保留第一个数组的值,忽略后续数组的重复键值。
$result = $array1 + $array2; print_r($result); // 输出:Array ( [a] => 1 [b] => 2 [c] => 3 [d] => 6 )
区别:array_merge() 是“后覆盖前”, 是“前覆盖后”,需根据需求选择。
累加合并:手动遍历或 array_reduce
如果需要相同键的值累加(如统计销量、求和),array_merge() 和 无法满足需求,需手动处理。
(1)手动遍历法
初始化一个空数组,遍历所有待合并数组,对每个键名检查是否已存在,存在则累加,否则直接赋值。
function sumMergeArrays(...$arrays) {
$result = [];
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (isset($result[$key])) {
$result[$key] += $value; // 累加
} else {
$result[$key] = $value;
}
}
}
return $result;
}
$result = sumMergeArrays($array1, $array2);
print_r($result);
// 输出:Array ( [a] => 5 [b] => 7 [c] => 3 [d] => 6 )
(2)array_reduce 函数
array_reduce 可通过回调函数逐步合并数组,适合函数式编程风格。
function sumMergeReducer($carry, $array) {
foreach ($array as $key => $value) {
if (isset($carry[$key])) {
$carry[$key] += $value;
} else {
$carry[$key] = $value;
}
}
return $carry;
}
$result = array_reduce([$array1, $array2], 'sumMergeReducer', []);
print_r($result);
// 输出:Array ( [a] => 5 [b] => 7 [c] => 3 [d] => 6 )
多维数组的相同键值合并:按某键分组聚合
需求场景
多维数组(如二维数组)中,可能需要根据某个“主键”(如 id、user_id)合并子数组,并对子数组的特定字段进行累加或拼接。
$data = [
['id' => 1, 'name' => 'Alice', 'score' => 85],
['id' => 2, 'name' => 'Bob', 'score' => 90],
['id' => 1, 'name' => 'Alice', 'score' => 10], // 与第一条同id,合并score
['id' => 3, 'name' => 'Charlie', 'score' => 75],
];
期望结果:相同 id 的记录合并,score 累加,name 保持一致(假设相同 id 的 name 相同)。
// 期望结果:
[
['id' => 1, 'name' => 'Alice', 'score' => 95],
['id' => 2, 'name' => 'Bob', 'score' => 90],
['id' => 3, 'name' => 'Charlie', 'score' => 75],
]
实现方法:分组 + 遍历合并
核心思路是:以主键(如 id)为索引,将相同主键的记录分组,再遍历分组合并目标字段。
(1)使用 array_column 提取主键
先提取所有主键(id),通过 array_unique 去重,得到唯一的主键列表。
$uniqueIds = array_unique(array_column($data, 'id'));
(2)遍历主键,合并对应记录
初始化空数组,遍历每个唯一主键,从原数据中筛选出该主键的所有记录,合并目标字段(如 score 累加)。
$mergedData = [];
foreach ($uniqueIds as $id) {
// 筛选当前id的所有记录
$group = array_filter($data, fn($item) => $item['id'] === $id);
// 初始化合并后的记录(取第一条的name)
$merged = ['id' => $id, 'name' => $group[0]['name']];
// 遍历分组,累加score
foreach ($group as $item) {
$merged['score'] = ($merged['score'] ?? 0) + $item['score'];
}
$mergedData[] = $merged;
}
print_r($mergedData);
(3)优化:使用 array_reduce 分组
更高效的方式是直接用 array_reduce 按主键分组,避免多次遍历。
$grouped = array_reduce($data, function ($carry, $item) {
$id = $item['id'];
if (!isset($carry[$id])) {
$carry[$id] = [
'id' => $id,
'name' => $item['name'],
'score' => 0,
];
}
$carry[$id]['score'] += $item['score'];
return $carry;
}, []);
// 将分组后的值转为索引数组
$mergedData = array_values($grouped);
print_r($mergedData);
// 输出:Array ( [0] => Array ( [id] => 1 [name] => Alice [score] => 95 ) [1] => Array ( [id] => 2 [name] => Bob [score] => 90 ) [2] => Array ( [id] => 3 [name] => Charlie [score] => 75 ) )
复杂场景:多维数组嵌套合并
如果数组是多层嵌套的(如三维数组),且需要递归合并相同键值,需使用递归或内置的 array_replace_recursive 函数。
array_replace_recursive 函数
array_replace_recursive 用于递归合并数组,当键名重复时,会递归合并子数组(而不是直接覆盖)。
$array1 = ['a' => 1, 'b' => ['c' => 2, 'd' => 3]]; $array2 = ['b' => ['c' => 4, 'e' => 5], 'f' => 6]; $result = array_replace_recursive($array1, $array2); print_r($result); // 输出:Array ( [a] => 1 [b] => Array ( [c] => 4 [d] => 3 [e] => 5 ) [f] => 6 )
注意:array_replace_recursive 会递归合并所有层级的子数组,如果希望某一级直接覆盖,需改用 array_replace(非递归)。
自定义递归合并
如果合并逻辑更复杂(如仅合并特定字段),需自定义递归函数,合并两个多维数组,仅对 score 字段累加:
function recursiveMerge($array1, $array2) {
$result = $array1;
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($result[$key]) && is_array($result[$key])) {
$result[$key] = recursiveMerge($result[$key], $value);
} elseif ($key === 'score' && isset($result[$key])) {
$result[$key] += $value; // 仅累加score
} else {
$result[$key] = $value;
}
}
return $result;
}
$array1 = ['user' => ['id' => 1, 'score' => 10, 'info' => ['age' => 20]]];
$array2 = ['user' => ['score' => 5, 'info' => ['gender' => 'male']], 'status' => 'active'];
$result = recursiveMerge($array1, $array2);
print_r($result);
// 输出:Array ( [user] => Array ( [id] => 1 [score] => 15 [info] => Array ( [age] => 20 [gender] => male ) ) [status] => active )
实际应用场景举例
电商订单:合并同一商品的销量
假设从多个订单接口获取商品销量数据,需合并同一商品的销量:
$order1 = ['product_id' => 101, 'quantity' => 2];
$order2 = ['product_id' => 102, 'quantity' => 1];
$order3 = ['product_id' => 101, 'quantity' => 3];
$orders = [$order1, $order2, $order3];
$merged = array_reduce($orders, function ($carry, $order) {
$id = $order['product_id'];
if (!isset($carry[$id])) {
$carry[$id] = ['product_id' => $id, 'quantity' => 0];
}
$carry[$id]['quantity'] += $order['quantity'];
return $carry;
}, []);
print_r(array_values($merged));
// 输出:Array ( [0] => Array ( [product_id] => 101 [quantity] => 5 ) [1] => Array ( [product_id] => 102 [quantity] => 1 ) )
配置文件合并:覆盖默认配置
开发中常需合并默认配置和用户自定义配置,用户配置需覆盖默认值:
$defaultConfig = [
'db' => ['host' => 'localhost', 'port' => 3306],
'cache' => ['enabled' => true, 'ttl' => 3600],
];
$userConfig = [
'db' => ['host' => 'example.com', 'user' => 'root'],
'debug' => true,
];
$mergedConfig = array_replace_recursive($defaultConfig, $userConfig);
print_r($mergedConfig);
// 输出:Array ( [db] => Array ( [host] => example.com [port] => 3306 [user] => root ) [cache] => Array ( [enabled] => true [ttl] => 3600 ) [debug] => true )
注意事项
-
键名数据类型:PHP中数字键(
1)和字符串键('1')被视为不同键,合并时需注意类型一致。$array = [1 => 'a', '1' => 'b']; print_r($array); // 输出:Array ( [1] => b )
-
性能考虑:大数据量时,
array_reduce和手动遍历的性能优于多次调用array_merge,建议优先使用array_reduce或分组合并。 -
数据类型转换:累加时需确保值的类型一致(如字符串和数字相加会拼接),必要时需类型转换:
$array = ['a' => '10', 'b' => 20]; $result = array_reduce([$array], function ($carry, $item) { foreach ($item as $key => $value) { $carry[$key] = (int)$value + ($carry[$key] ?? 0); } return $carry; }, []); print_r($result); // 输出:Array ( [a] => 10 [b] => 20 )
PHP中相同键值的合并需根据场景选择方法:
- 一维数组覆盖合并:
array_merge(后覆盖前)或 (前覆盖后); - 一维数组累加合并:手动遍历或
array_reduce; - 多维数组分组合并:按主键分组后遍历合并,或
array_reduce分组; - 嵌套数组递归合并:
array_replace_recursive或自定义递归函数。
理解每种方法的原理和适用场景,才能高效解决实际开发中的合并需求。