
本文详解如何对多维数组按指定键进行**保持原始顺序的连续分组**——即每当目标键值发生变化时,自动创建新分组并追加序号后缀(如 `1.1`、`1.2`、`4.1`),而非传统哈希式聚合。
在实际开发中,我们常需对数据进行分组处理,但标准的 foreach + $result[$key][] = $item 方式仅实现去重归并(即所有相同键值的元素合并为一个子数组),而无法保留原始顺序中的“逻辑段落”结构。例如,在时间序列报表、工单流水或日志分析场景中,业务要求将相邻且键值相同的连续块视为独立分组(哪怕该键值此前已出现过),此时就需要一种“控制断点(Control Break)”式的分组策略。
核心思路是:遍历数组时,持续跟踪当前键值与前一项的差异;一旦发生变更,即为新分组起点,并为该键值维护一个递增计数器,最终以 {$key}.{$counter} 构建唯一分组键。
以下为完整、健壮的实现代码:
function groupByConsecutiveKey(array $items, string $key): array
{
if (empty($items)) {
return [];
}
$result = [];
$counter = []; // 每个键值对应的当前分组序号
$prevValue = null;
foreach ($items as $item) {
$currentValue = $item[$key] ?? null;
// 当前键值与前一项不同 → 触发新分组
if ($currentValue !== $prevValue) {
$counter[$currentValue] = ($counter[$currentValue] ?? 0) + 1;
}
$groupKey = $currentValue . '.' . $counter[$currentValue];
$result[$groupKey][] = $item;
$prevValue = $currentValue;
}
return $result;
}
// 使用示例(基于问题中的原始数据结构)
$report_items = [
['id' => 972, 'user_id' => 2, 'user_field_48' => 1, 'project' => '100 — NLO', 'duration' => '1:00', 'grouped_by' => 1],
['id' => 644, 'user_id' => 2, 'user_field_48' => 4, 'project' => '123 — QHV', 'duration' => '15:00', 'grouped_by' => 4],
['id' => 631, 'user_id' => 2, 'user_field_48' => 4, 'project' => '', 'duration' => '-5:00', 'grouped_by' => 4],
['id' => 630, 'user_id' => 2, 'user_field_48' => 1, 'project' => '', 'duration' => '22:00', 'grouped_by' => 1],
['id' => 971, 'user_id' => 2, 'user_field_48' => 1, 'project' => '100 — NLO', 'duration' => '1:00', 'grouped_by' => 1],
['id' => 973, 'user_id' => 2, 'user_field_48' => 1, 'project' => '100 — NLO', 'duration' => '1:00', 'grouped_by' => 1],
['id' => 974, 'user_id' => 2, 'user_field_48' => 1, 'project' => '100 — NLO', 'duration' => '1:00', 'grouped_by' => 1],
];
$grouped = groupByConsecutiveKey($report_items, 'grouped_by');
print_r($grouped);✅ 输出效果(关键分组键示意):
立即学习“PHP免费学习笔记(深入)”;
- 1.1 → 索引 0 的 [grouped_by => 1] 开始的第一个连续段(仅含 id=972)
- 4.1 → 索引 1–2 的 [grouped_by => 4] 连续段(含 id=644,631)
- 1.2 → 索引 3–6 的 [grouped_by => 1] 第二个连续段(含 id=630,971,973,974)
? 注意事项与最佳实践:
- 键值类型兼容性:该方案支持整型、字符串甚至 null(通过 === 严格比较保障准确性);
- 空键安全:使用 $item[$key] ?? null 防止未定义索引警告,生产环境建议提前校验数据结构;
- 性能友好:单次遍历,时间复杂度 O(n),无嵌套循环或重复查找;
- 可扩展性:函数封装便于复用,支持任意字段名(如 'user_field_48' 或 'project');
- 避免常见陷阱:勿直接用 array_values() 重排结果——这会破坏 1.1/1.2 的语义顺序;若需按数字排序分组键,可后续调用 ksort($result, SORT_NATURAL)。
此方法本质是将“分组”从静态映射升级为状态感知的流式切片,精准匹配报表生成、PDF 分页、前端折叠面板等需保持上下文连贯性的业务需求。掌握它,你便拥有了处理时序化分组问题的标准化工具。











