最简洁方式是array_column+array_count_values,适用于字段齐全场景;遇缺失键需先过滤;嵌套字段或数据不稳时用array_reduce更安全;大数据量需关注内存,复杂逻辑优先用foreach。

用 array_column + array_count_values 统计字段频次
这是最简洁、最常用的方式,适用于「每个子数组都有该字段」的场景。PHP 5.5+ 支持 array_column,它能直接提取多维数组中指定键的所有值,再交给 array_count_values 计数。
常见错误是传错参数顺序或忽略缺失键导致 Warning —— array_column 遇到子数组不含目标键时会返回 null,而 array_count_values 不接受 null 值,直接报错 Warning: array_count_values(): Can only count string and integer values。
- 确保所有子数组都含目标字段,或先过滤:用
array_filter($arr, fn($item) => isset($item['status'])) -
array_column($arr, 'status')提取所有'status'值;array_count_values(...)返回键为字段值、值为出现次数的关联数组 - 如果字段是嵌套的(如
user.name),array_column不支持点号路径,得换方法
处理嵌套字段或不规则结构用 array_reduce
当要统计 $item['meta']['category'] 或某些子数组缺字段、类型不统一时,array_reduce 更可控。它逐个遍历,手动提取并累加,不会因缺失键崩溃。
性能上比 array_column 略低,但逻辑清晰、容错强,适合生产环境不确定数据质量的场景。
立即学习“PHP免费学习笔记(深入)”;
- 初始化累加器为
[],每次检查isset($item['meta']['category'])再提取 - 避免用
$item['meta']['category'] ?? ''直接取值——若$item['meta']是字符串而非数组,会触发Warning: Trying to access array offset on value of type string - 示例片段:
$counts = array_reduce($data, function($carry, $item) { if (is_array($item['meta'] ?? []) && isset($item['meta']['category'])) { $val = $item['meta']['category']; $carry[$val] = ($carry[$val] ?? 0) + 1; } return $carry; }, []);
需要去重计数或按条件过滤时别硬套 array_count_values
比如「统计 status 为 active 的用户数」或「统计不同 category 下的平均 price」,这时候 array_count_values 就不够用了。它只做纯频次统计,不支持条件分支或聚合计算。
强行用 array_filter + array_column + array_count_values 组合虽可行,但可读性差、嵌套深,且每次过滤都要遍历全数组。
- 优先考虑
foreach手动循环:一次遍历完成判断 + 提取 + 累加,性能和可维护性更好 - 如果后续还要做其他聚合(如求和、分组平均),直接在循环里加变量,比拆成多个
array_*调用更高效 - 注意字符串比较陷阱:数据库来的
'1'和整型1在array_count_values中算不同键;手动循环时可用(string)$val或(int)$val统一类型
大数组下内存和性能的实际影响
array_column 会生成一个新数组存放全部字段值,对 10 万+ 元素的数组,可能额外占用几十 MB 内存。而 foreach 或 array_reduce 是流式处理,内存占用稳定在常量级。
不是所有情况都要优化,但如果你的脚本常驻运行(如 CLI 守护进程)或处理日志类大数据,这点必须留意。
- 用
memory_get_usage(true)对比两种方式的峰值内存,别凭感觉 -
array_count_values内部用哈希表实现,时间复杂度 O(n),和手动循环一样快;慢的是前面的array_column拷贝开销 - 如果只是偶尔调用、数组小于 1 万,选最易读的写法就行,不用过早优化



















