JavaScript实现二维数组动态表头分组聚合(支持sum等操作)
高效实现动态分组聚合函数GenerateGroupByData
针对你提出的需求,我整理了一个简洁高效的实现方案——核心思路是用对象作为分组映射表,通过一次遍历完成分组和聚合,同时完美支持动态分组字段和多种聚合操作。
完整实现(JavaScript)
const sqlData = [ ['a','b','c','d','e','f','g'], ['aa','10/11/2020','##',2,3,4,5], ['bb','10/12/2020','$$',23,13,14,95], ['aa','10/12/2020','!!',21,23,24,58], ['aa','10/12/2020','##',22,13,44,55], ['aa','10/11/2020','$$',12,33,45,52], ['bb','10/11/2020','!!',52,32,49,52], ['aa','10/11/2020','!!',72,53,44,51], ['bb','10/11/2020','##',12,3,43,65], ['bb','10/13/2020','##',112,63,4,85], ['bb','10/13/2020','$$',22,63,24,95], ['bb','10/12/2020','$$',32,73,44,50], ['bb','10/13/2020','$$',52,388,24,50], ['bb','10/13/2020','$$',72,233,41,52], ['bb','10/13/2020','##',82,123,46,57], ['bb','10/13/2020','!!',92,763,47,58], ]; function GenerateGroupByData(CurrentData, groupbyheaders, ops = 'sum') { // 拆分表头和数据行 const [headers, ...rows] = CurrentData; // 获取分组字段的索引,以及需要聚合的字段索引 const groupIndices = groupbyheaders.map(header => headers.indexOf(header)); const aggregateIndices = headers .map((_, idx) => groupbyheaders.includes(headers[idx]) ? null : idx) .filter(idx => idx !== null); // 定义聚合操作字典,方便快速调用和扩展 const aggregateOps = { sum: (acc, val) => acc + val, average: (acc, val, count) => (acc * (count - 1) + val) / count, max: (acc, val) => Math.max(acc, val), min: (acc, val) => Math.min(acc, val) }; // 兜底:如果传入的操作不存在,默认用sum const activeOp = aggregateOps[ops] || aggregateOps.sum; // 一次遍历完成分组聚合 const groups = rows.reduce((acc, row) => { // 生成唯一分组键:用特殊分隔符拼接分组字段值,避免冲突 const groupKey = groupIndices.map(idx => row[idx]).join('|'); if (!acc[groupKey]) { // 初始化分组数据:保存分组字段值,聚合字段初始化为当前行的值 acc[groupKey] = { groupValues: groupIndices.map(idx => row[idx]), aggregates: aggregateIndices.map(idx => row[idx]), count: 1 // 用于平均值计算的计数 }; } else { // 更新聚合字段 acc[groupKey].aggregates = acc[groupKey].aggregates.map((currVal, aggIdx) => { const rowVal = row[aggregateIndices[aggIdx]]; return activeOp(currVal, rowVal, acc[groupKey].count + 1); }); acc[groupKey].count++; } return acc; }, {}); // 转换为要求的二维数组格式 const resultHeaders = [...groupbyheaders, ...headers.filter(h => !groupbyheaders.includes(h))]; const resultRows = Object.values(groups).map(group => [...group.groupValues, ...group.aggregates]); return [resultHeaders, ...resultRows]; } // 示例调用:按a、b分组求和 console.log(GenerateGroupByData(sqlData, ['a', 'b'])); // 示例调用:按a、c分组求平均值 console.log(GenerateGroupByData(sqlData, ['a', 'c'], 'average'));
核心亮点说明
- 高效低复杂度:只用一次
reduce遍历数据行,时间复杂度为O(n)(n是数据行数),比多次遍历的方案性能好很多 - 完全动态适配:不管你传入哪些分组字段,都会自动从表头匹配索引,不需要硬编码字段位置
- 易扩展聚合操作:如果需要新增聚合逻辑(比如
count、median),直接在aggregateOps字典里加对应函数即可 - 鲁棒性处理:如果传入了不支持的聚合操作,会自动兜底用
sum;若要更严谨,还可以加分组字段存在性校验 - 分组键唯一性:用
|作为分隔符拼接分组值,如果你担心字段值里包含|,可以换成\u0000这种不可见字符,避免键冲突
内容的提问来源于stack exchange,提问作者Shivam Sahil
相关产品推荐
相关产品推荐

