
本文介绍一种高效、可读性强的 php 解决方案:遍历两次原数组,首次统计各特殊键(如 'aa'/'bb'/'cc')下值的出现频次,第二次根据频次将重复值移入统一子数组 'yy' 并从原位置删除。
本文介绍一种高效、可读性强的 php 解决方案:遍历两次原数组,首次统计各特殊键(如 'aa'/'bb'/'cc')下值的出现频次,第二次根据频次将重复值移入统一子数组 'yy' 并从原位置删除。
在处理业务数据时,常需对多维数组中某些关键字段(如 ID、编码、状态码)进行去重归类。但本需求并非简单删除重复项,而是识别出在全局范围内出现次数 ≥2 的值,并将这些“非唯一”的键值对统一收编到新键 'yy' 下,同时保留在原数组中仅出现一次的键值(即“唯一值”仍保留在顶层)。
该逻辑的核心在于:区分“全局重复”与“单行独立”。例如,'aa' => 1234 在全部三行中均存在 → 属于重复,应移入 'yy';而 'bb' => 1000 仅在第 0 行出现 → 唯一,保留在顶层。
实现分两步完成:
构建频次查找表(lookup)
使用嵌套关联数组$lookup[$key][$value]记录每个特殊键对应各值的出现次数。注意:我们用??= -1; ++$lookup[...];初始化为-1后自增,是为了让首次出现计为0(即0表示唯一,≥1表示重复),从而避免额外判断。二次遍历并重构每行结构
对每行数据,检查其specialkeys中每个键对应的值是否在lookup中计数 ≥1。若是,则将其键值对写入$row['yy']并unset()原键。
以下是完整可运行代码(含注释与典型用例):
$specialkeys = ['aa', 'bb', 'cc'];
$myarray = [
['aa' => 1234, 'bb' => 1000, 'cc' => 2345, 'xx' => 'wsad'],
['aa' => 1234, 'bb' => 2000, 'cc' => 2345, 'xx' => 'wsad'],
['aa' => 1234, 'bb' => 2000, 'cc' => 2345, 'xx' => 'wsad']
];
// 步骤1:构建键值频次映射表
$lookup = [];
foreach ($myarray as $row) {
foreach ($specialkeys as $key) {
$val = $row[$key];
$lookup[$key][$val] ??= -1; // 首次设为 -1,后续 ++ 得 0(表示唯一)
$lookup[$key][$val]++;
}
}
// 步骤2:重构每行 —— 将重复值移入 'yy'
foreach ($myarray as &$row) {
foreach ($specialkeys as $key) {
$val = $row[$key];
// 若该值在全局出现 ≥2 次(即 lookup 计数 ≥1),则迁移
if ($lookup[$key][$val] >= 1) {
$row['yy'][$key] = $val;
unset($row[$key]);
}
}
}
// 注意:必须解除引用,避免意外修改
unset($row);
print_r($myarray);
✅ 输出结果符合预期:
- 第 0 行:
'aa'=>1234和'cc'=>2345全局重复 → 进入'yy';'bb'=>1000唯一 → 保留顶层; - 第 1、2 行:
'aa'/'bb'/'cc'均重复 → 全部进入'yy',仅剩'xx'和'yy'。
⚠️ 注意事项:
- 该方案时间复杂度为 O(2×n×m),其中 n 是数组长度,m 是
$specialkeys数量,性能优异,适合千级以内数据; -
&$row引用需配合unset($row)防止后续误用; - 若
$specialkeys中某键在某行不存在,会触发 Notice;生产环境建议增加isset($row[$key])校验; -
'yy'键始终以关联数组形式存在(即使为空),如需严格空值过滤,可在循环后添加if (empty($row['yy'])) unset($row['yy']);。
此方法不依赖外部库,纯原生 PHP 实现,逻辑清晰、易于调试与扩展,是处理此类“条件性键迁移”问题的推荐实践。











