php相同键值合并

admin 55 0
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 )

多维数组的相同键值合并:按某键分组聚合

需求场景

多维数组(如二维数组)中,可能需要根据某个“主键”(如 iduser_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 保持一致(假设相同 idname 相同)。

// 期望结果:
[
    ['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 )

注意事项

  1. 键名数据类型:PHP中数字键(1)和字符串键('1')被视为不同键,合并时需注意类型一致。

    $array = [1 => 'a', '1' => 'b'];
    print_r($array); // 输出:Array ( [1] => b )
  2. 性能考虑:大数据量时,array_reduce 和手动遍历的性能优于多次调用 array_merge,建议优先使用 array_reduce 或分组合并。

  3. 数据类型转换:累加时需确保值的类型一致(如字符串和数字相加会拼接),必要时需类型转换:

    $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 或自定义递归函数。

理解每种方法的原理和适用场景,才能高效解决实际开发中的合并需求。

标签: #键值 #合并