如何在JavaScript中基于动态分组创建层级数据并实现值的向上汇总,同时解决大数据量下浏览器卡顿问题
动态分组构建层级汇总数据结构的问题
我需要实现一个功能:根据动态指定的分组字段创建层级化的数据结构,同时将数值字段向上汇总到顶层节点。但目前处理大量数据时会出现浏览器卡顿的情况,而且我自己写的函数没法正常工作。下面是相关的细节:
示例输入数据
var data = [ { "country":"Spain", "orderNumber":10394, "year":2018, "countrycode":"es", "quantityOrdered":30, "priceEach":60.28, "productName":"1950's Chicago Surface Lines Streetcar", "productLine":"Trains", "sortkey":3, "productCode":"S32_3207", "month":"March", "extendedPrice":1808.4, "orderDate":"2018-03-15 00:00:00" }, { "country":"France", "orderNumber":10395, "year":2018, "countrycode":"fr", "quantityOrdered":32, "priceEach":105.33, "productName":"1972 Alfa Romeo GTA", "productLine":"Classic Cars", "sortkey":3, "productCode":"S10_4757", "month":"March", "extendedPrice":3370.56, "orderDate":"2018-03-17 00:00:00" }, { "country":"France", "orderNumber":10395, "year":2018, "countrycode":"fr", "quantityOrdered":33, "priceEach":69.12, "productName":"2001 Ferrari Enzo", "productLine":"Classic Cars", "sortkey":3, "productCode":"S12_1108", "month":"March", "extendedPrice":2280.96, "orderDate":"2018-03-17 00:00:00" }, { "country":"France", "orderNumber":10395, "year":2018, "countrycode":"fr", "quantityOrdered":46, "priceEach":123.76, "productName":"Diamond T620 Semi-Skirted Tanker", "productLine":"Trucks and Buses", "sortkey":3, "productCode":"S50_1392", "month":"March", "extendedPrice":5692.96, "orderDate":"2018-03-17 00:00:00" }, { "country":"France", "orderNumber":10395, "year":2018, "countrycode":"fr", "quantityOrdered":45, "priceEach":199.49, "productName":"1962 City of Detroit Streetcar", "productLine":"Trains", "sortkey":3, "productCode":"S50_1514", "month":"March", "extendedPrice":8977.05, "orderDate":"2018-03-17 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":33, "priceEach":185.13, "productName":"1969 Ford Falcon", "productLine":"Classic Cars", "sortkey":3, "productCode":"S12_3891", "month":"March", "extendedPrice":6109.29, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":33, "priceEach":159.81, "productName":"1903 Ford Model A", "productLine":"Vintage Cars", "sortkey":3, "productCode":"S18_3140", "month":"March", "extendedPrice":5273.73, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":24, "priceEach":89.75, "productName":"Collectable Wooden Train", "productLine":"Trains", "sortkey":3, "productCode":"S18_3259", "month":"March", "extendedPrice":2154, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":45, "priceEach":105.32, "productName":"1904 Buick Runabout", "productLine":"Vintage Cars", "sortkey":3, "productCode":"S18_4522", "month":"March", "extendedPrice":4739.4, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":49, "priceEach":116.75, "productName":"18th century schooner", "productLine":"Ships", "sortkey":3, "productCode":"S24_2011", "month":"March", "extendedPrice":5720.75, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":27, "priceEach":83.2, "productName":"1912 Ford Model T Delivery Wagon", "productLine":"Vintage Cars", "sortkey":3, "productCode":"S24_3151", "month":"March", "extendedPrice":2246.4, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":37, "priceEach":90.57, "productName":"1940 Ford Delivery Sedan", "productLine":"Vintage Cars", "sortkey":3, "productCode":"S24_3816", "month":"March", "extendedPrice":3351.09, "orderDate":"2018-03-23 00:00:00" }, { "country":"USA", "orderNumber":10396, "year":2018, "countrycode":"us", "quantityOrdered":39, "priceEach":66.67, "productName":"The Schooner Bluenose", "productLine":"Ships", "sortkey":3, "productCode":"S700_1138", "month":"March", "extendedPrice":2600.13, "orderDate":"2018-03-23 00:00:00" }, { "country":"France", "orderNumber":10397, "year":2018, "countrycode":"fr", "quantityOrdered":32, "priceEach":80.55, "productName":"The Mayflower", "productLine":"Ships", "sortkey":3, "productCode":"S700_1938", "month":"March", "extendedPrice":2577.6, "orderDate":"2018-03-28 00:00:00" } ];
我编写的问题代码
我的分组和汇总字段都是动态的,比如:
var groups = ['country', 'productLine', 'month']; // 动态分组字段 var sum = ['priceEach']; // 动态汇总字段 function createGroup (groups, data, sum, childNode) { let [primaryGroup, ...rest] = groups; let groupedData = data.reduce((acc, current) => { let chunk = { 'Name': current[primaryGroup], [primaryGroup]: current[primaryGroup], [sum]: data.filter(item => item[primaryGroup] === current[primaryGroup]) .map(el => el[sum]) .reduce((total, current) => total + current), ...(rest.length > 0 ? {[groups[childNode]]: createGroup(rest, data, sum,childNode+1 )} : {}) } acc.push(chunk) return acc }, []) .reduce((acc, current) => { const x = acc.find(item => item[primaryGroup] === current[primaryGroup]) return !x ? acc.concat([current]) : acc }, []) return groupedData; } const tree = createGroup(groups,data,sum, 1);
这个函数有两个主要问题:一是没法正确生成层级结构和汇总数值,二是处理大量数据时因为重复遍历(比如filter和find)导致浏览器卡顿。
期望的输出格式
我希望得到类似下面的层级结构,数值会向上汇总到顶层节点(示例仅展示部分数据):
[ { "country": [ { "Name":"Spain", "priceEach":60.28, "productLine": [ { "Name":"Trains", "priceEach":60.28, "month": [ { "Name":"March", "priceEach":60.28 } ] } ] } ] }, { "country": [ { "Name":"France", "priceEach":578.25, "productLine": [ { "Name":"Classic Cars", "priceEach":174.45, "month": [ { "Name":"March", "priceEach":174.45 } ] }, { "Name":"Trucks and Buses", "priceEach":123.76, "month": [ { "Name":"March", "priceEach":123.76 } ] }, { "Name":"Trains", "priceEach":199.49, "month": [ { "Name":"March", "priceEach":199.49 } ] }, { "Name":"Ships", "priceEach":80.55, "month": [ { "Name":"March", "priceEach":80.55 } ] } ] } ] } ]
解决方案
下面是修复后的函数,解决了层级生成、数值汇总的问题,同时优化了性能,避免大数据量下的卡顿:
var groups = ['country', 'productLine', 'month']; var sumFields = ['priceEach']; function createHierarchicalData(groups, data, sumFields) { // 递归构建层级的核心函数 function buildLevel(currentGroups, currentData) { if (currentGroups.length === 0) return []; const [currentGroup, ...remainingGroups] = currentGroups; // 用对象做分组,比数组find高效N倍,适合大数据量 const groupsMap = {}; // 遍历一次数据完成分组和初始汇总 currentData.forEach(item => { const key = item[currentGroup]; if (!groupsMap[key]) { // 初始化分组节点 groupsMap[key] = { Name: key, [currentGroup]: key }; // 初始化所有汇总字段为0 sumFields.forEach(field => { groupsMap[key][field] = 0; }); // 存储当前分组下的子数据,用于递归 groupsMap[key]._childData = []; } // 累加汇总字段数值 sumFields.forEach(field => { groupsMap[key][field] += item[field]; }); groupsMap[key]._childData.push(item); }); // 转换为数组,并处理子层级 return Object.values(groupsMap).map(groupNode => { const resultNode = { ...groupNode }; delete resultNode._childData; // 移除临时存储的子数据 if (remainingGroups.length > 0) { // 递归构建子层级,只传递当前分组下的子数据,减少遍历范围 resultNode[remainingGroups[0]] = buildLevel(remainingGroups, groupNode._childData); } return resultNode; }); } // 构建顶层结构 const topLevel = buildLevel(groups, data); return groups.length > 0 ? [{ [groups[0]]: topLevel }] : []; } // 使用示例 const tree = createHierarchicalData(groups, data, sumFields); console.log(JSON.stringify(tree, null, 2));
关键优化和修复点:
- 性能优化:使用对象
groupsMap做分组,只遍历一次数据完成分组和汇总,避免了原代码中filter和find带来的多次重复遍历,大幅提升大数据量下的处理速度,解决浏览器卡顿问题。 - 正确的层级生成:递归时只传递当前分组下的数据子集,确保子层级的汇总和分组是基于父节点的范围,而不是整个数据集。
- 支持多汇总字段:
sumFields可以是数组,支持同时汇总多个数值字段。 - 正确的汇总逻辑:遍历数据时直接累加当前项的数值,而不是重新筛选数据再求和,逻辑更准确且高效。
内容的提问来源于stack exchange,提问作者Mayur
相关产品推荐
相关产品推荐

