Laravel 如何对groupBy结果使用reduce高效统计供应商总金额
优化后实现方案
完全使用Laravel集合内置方法,不需要手动嵌套循环,性能和可读性都优于原有实现:
// 注意原方法名generateSupplierDate是笔误,修正为generateSupplierData更合理 private function generateSupplierData(Collection $collection, array $suppliers = []): Collection { return $collection->pluck('items') ->flatten() ->whereIn('supplier', $suppliers) ->groupBy('supplier') ->map(function ($group, $supplierName) { return [ 'supplier' => $supplierName, 'totalCost' => $group->sum('quantity') ]; }) ->values(); }
逻辑说明
- 原代码存在隐性问题:方法声明返回
Collection类型,但最终返回了普通数组,运行时会抛出类型错误,优化后的代码直接返回集合实例完全符合声明要求。 - 原有打平数组、过滤指定供应商的逻辑保留,这部分是必要的前置处理。
- 按
supplier分组后直接调用集合的sum()方法对分组内的quantity字段求和,不需要手动循环累加,Laravel底层优化过的聚合方法执行效率优于手动嵌套循环,代码可读性也更高。 - 用
map()方法直接把分组转换成你需要的键名格式,最后调用values()重置数组键为连续的数字索引,返回的集合转JSON后和你要求的输出结构完全匹配。
reduce实现版本(性能更高)
如果你需要用reduce实现的话可以参考以下写法,整个过程只需要一次遍历,不需要分组阶段,性能会略高于分组实现:
private function generateSupplierData(Collection $collection, array $suppliers = []): Collection { return $collection->pluck('items') ->flatten() ->whereIn('supplier', $suppliers) ->reduce(function ($carry, $item) { // 查找当前供应商是否已经在结果中存在 $exists = $carry->firstWhere('supplier', $item->supplier); if ($exists) { $exists['totalCost'] += $item->quantity; } else { $carry->push([ 'supplier' => $item->supplier, 'totalCost' => $item->quantity ]); } return $carry; }, collect()); }
你有JS开发基础的话理解起来会很简单,Laravel集合的方法逻辑和JS数组方法基本一致,只是部分API命名有差异,reduce的用法和JS几乎完全相同,只是初始值需要用
collect()生成空集合而非空数组。
内容的提问来源于stack exchange,提问作者Riza Khan
相关产品推荐
相关产品推荐

