用 foreach 手动构建分组数组最稳妥,兼容所有 php 版本;按字段键名分组时用 $key = $item['field'] ?? 'default' 避免未定义索引,嵌套层级需逐层 isset 判断或统一用 ??,多级分组用双层 foreach 实现,不推荐 array_group_by 等第三方函数。

直接按某个键对多维数组分组,用 foreach 手动构建新数组最稳,不依赖扩展、不踩坑、兼容所有 PHP 版本(包括 php 8.5.5)。
用 foreach 按字段键名分组(最常用)
这是最直观、最可控的方式。比如你有一组订单数据,想按 supplier_id 分组:
常见错误是试图用 array_reduce 或 array_column 直接“一键分组”,但它们要么不支持嵌套结构,要么会丢掉原始键或打乱顺序。
- 遍历原数组,每次取
$item['supplier_id']作为新数组的键 - 用
isset($grouped[$key])判断分组是否存在,避免未定义索引警告 - 直接追加:
$grouped[$key][] = $item,保留每个子项的完整结构 - 不推荐用
array_push替代[] =,前者多一次函数调用,无必要
示例代码:
$grouped = [];
foreach ($orders as $item) {
$key = $item['supplier_id'] ?? 'unknown';
if (!isset($grouped[$key])) {
$grouped[$key] = [];
}
$grouped[$key][] = $item;
}
分组键来自嵌套层级(如 $item['meta']['category'])
当要分组的字段不在第一层,而是藏在子数组里时,硬写 $item['meta']['category'] 容易触发 Notice: Undefined index。
正确做法是用空合并操作符 ?? 提供默认值,并封装成小函数防重复:
- 先检查
isset($item['meta']) && isset($item['meta']['category']),再取值 - 或统一用
$item['meta']['category'] ?? 'other'(PHP 7+ 支持) - 如果嵌套深(如
$item['data']['info']['type']),建议抽成辅助函数,避免满屏?? - 不要用
@抑制错误——它会掩盖真实问题,且影响性能
需要多级分组(比如先按 status,再按 region)
单层 foreach 不够用了,但也不必上递归函数。两层嵌套就能搞定:
- 外层按第一级键(如
status)分组 - 内层对每个子数组再按第二级键(如
region)分组 - 结果结构为
$grouped[$status][$region][] = $item - 注意:这种结构在后续
foreach遍历时,必须双层foreach,不能直接count($grouped)得到总条目数
容易被忽略的是:如果某条数据缺失任一级键,?? 'default' 要写在每一层取值时,而不是只在最外层兜底。
为什么不用现成的 array_group_by 函数?
网上流传的 array_group_by 实现(尤其带递归参数的版本)看着方便,但实际有隐患:
- 多数版本用
func_get_args()+call_user_func_array实现多级,PHP 8.4+ 已标记该用法为“不推荐” - 递归调用容易栈溢出,尤其当数组深度 > 10 或元素数 > 1000 时
- 返回结构不可预测:有的保留原始键,有的重索引,对接口消费方不友好
- 调试困难——出错时堆栈里全是
call_user_func_array,找不到原始数据位置
真要复用逻辑,建议封装成简单函数,只接受 $array 和 $key 两个参数,内部仍是干净的 foreach,别塞递归和反射。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











