如何用Underscore.js按URL及年月分组对象数组并计算平均值
需求与问题
需要对对象数组中的数据完成以下处理:
- 按
finalUrl分组 - 按年份-月份拆分数据
- 计算每组的指标总和,以及对应月份所有数据的平均值
原始单指标数据:
var getdataObj = [{ "finalUrl": "https://www.amazon.in/", "fetchTime": "2022-10-15T08:58:18.485Z", "audits": { "first-contentful-paint": { "displayValue": "1.2" } } }, { "finalUrl": "https://www.google.in/", "fetchTime": "2022-11-15T08:58:18.485Z", "audits": { "first-contentful-paint": { "displayValue": "6.2" } } }, { "finalUrl": "https://www.flipkart.in/", "fetchTime": "2022-10-15T08:58:18.485Z", "audits": { "first-contentful-paint": { "displayValue": "4.2" } } }, { "finalUrl": "https://www.amazon.in/", "fetchTime": "2022-10-15T08:58:18.485Z", "audits": { "first-contentful-paint": { "displayValue": "3.7" } } }, { "finalUrl": "https://www.google.in/", "fetchTime": "2022-12-15T08:58:18.485Z", "audits": { "first-contentful-paint": { "displayValue": "3.2" } } }]
现有代码存在的问题:
- 先按完整
fetchTime分组,导致同一月份不同日期的数据被拆分,无法合并为月份维度 - 未计算对应月份的总数据量和平均值
修正后的解决方案
1. 单指标(first-contentful-paint)场景
var groupedData = _.chain(getdataObj) // 第一步:按年份-月份分组(提取fetchTime前7位) .groupBy(item => item.fetchTime.substr(0, 7)) .map(function(monthGroup, monthKey) { // 计算当前月份的总数据量、指标总和与平均值 const monthTotalCount = monthGroup.length; const monthTotalValue = _.reduce(monthGroup, (acc, item) => { acc += parseFloat(item.audits['first-contentful-paint'].displayValue); return acc; }, 0); const monthAverage = monthTotalCount > 0 ? (monthTotalValue / monthTotalCount).toFixed(2) : 0; // 第二步:当前月份内按finalUrl分组,统计各站点数据 const urlGroups = _.chain(monthGroup) .groupBy('finalUrl') .map(function(urlGroup, urlKey) { const urlTotalValue = _.reduce(urlGroup, (acc, item) => { acc += parseFloat(item.audits['first-contentful-paint'].displayValue); return acc; }, 0); return { url: urlKey, totalValue: urlTotalValue, count: urlGroup.length }; }) .value(); return { month: monthKey, monthTotalCount: monthTotalCount, monthTotalValue: monthTotalValue, monthAverage: parseFloat(monthAverage), urlGroups: urlGroups }; }) .value(); // 输出结果 _.each(groupedData, function(monthData) { console.log(`月份: ${monthData.month}`); console.log(` 当月总数据量: ${monthData.monthTotalCount}`); console.log(` 当月指标总和: ${monthData.monthTotalValue}`); console.log(` 当月指标平均值: ${monthData.monthAverage}`); _.each(monthData.urlGroups, function(urlData) { console.log(` 站点: ${urlData.url}`); console.log(` 该站点当月数据量: ${urlData.count}`); console.log(` 该站点当月指标总和: ${urlData.totalValue}`); }); console.log('---'); });
2. 多指标(first-contentful-paint + largest-contentful-paint)场景
针对包含双指标的数据集,调整代码如下:
var data = [{ fetchTime: '2022-12-15T08:58:18.485Z', finalUrl: 'https://www.amazon.in/', audits: { "first-contentful-paint": { "displayValue": "3.4" }, "largest-contentful-paint": { "displayValue": "2.3" } } }, { fetchTime: '2022-12-15T08:58:18.485Z', finalUrl: 'https://www.google.in/', audits: { "first-contentful-paint": { "displayValue": "1.2" }, "largest-contentful-paint": { "displayValue": "3.4" } } }, { fetchTime: '2023-02-15T08:58:18.485Z', finalUrl: 'https://www.amazon.in/', audits: { "first-contentful-paint": { "displayValue": "6.2" }, "largest-contentful-paint": { "displayValue": "3.5" } } }, { fetchTime: '2022-11-15T08:58:18.485Z', finalUrl: 'https://www.flipkart.in/', audits: { "first-contentful-paint": { "displayValue": "4.2" }, "largest-contentful-paint": { "displayValue": "5.3" } } }, { fetchTime: '2023-01-15T08:58:18.485Z', finalUrl: 'https://www.google.in/', audits: { "first-contentful-paint": { "displayValue": "7.2" }, "largest-contentful-paint": { "displayValue": "8.3" } } }]; var groupedData = _.chain(data) .groupBy(item => item.fetchTime.substr(0, 7)) .map(function(monthGroup, monthKey) { const monthTotalCount = monthGroup.length; // 计算当月双指标的总和 const monthTotals = _.reduce(monthGroup, (acc, item) => { acc.fcp += parseFloat(item.audits['first-contentful-paint'].displayValue); acc.lcp += parseFloat(item.audits['largest-contentful-paint'].displayValue); return acc; }, { fcp: 0, lcp: 0 }); // 计算当月双指标的平均值 const monthAverages = { fcp: monthTotalCount > 0 ? (monthTotals.fcp / monthTotalCount).toFixed(2) : 0, lcp: monthTotalCount > 0 ? (monthTotals.lcp / monthTotalCount).toFixed(2) : 0 }; const urlGroups = _.chain(monthGroup) .groupBy('finalUrl') .map(function(urlGroup, urlKey) { const urlTotals = _.reduce(urlGroup, (acc, item) => { acc.fcp += parseFloat(item.audits['first-contentful-paint'].displayValue); acc.lcp += parseFloat(item.audits['largest-contentful-paint'].displayValue); return acc; }, { fcp: 0, lcp: 0 }); return { url: urlKey, count: urlGroup.length, totals: urlTotals }; }) .value(); return { month: monthKey, monthTotalCount: monthTotalCount, monthTotals: monthTotals, monthAverages: { fcp: parseFloat(monthAverages.fcp), lcp: parseFloat(monthAverages.lcp) }, urlGroups: urlGroups }; }) .value(); // 输出结果 _.each(groupedData, function(monthData) { console.log(`月份: ${monthData.month}`); console.log(` 当月总数据量: ${monthData.monthTotalCount}`); console.log(` 当月FCP总和: ${monthData.monthTotals.fcp},平均值: ${monthData.monthAverages.fcp}`); console.log(` 当月LCP总和: ${monthData.monthTotals.lcp},平均值: ${monthData.monthAverages.lcp}`); _.each(monthData.urlGroups, function(urlData) { console.log(` 站点: ${urlData.url}`); console.log(` 该站点当月数据量: ${urlData.count}`); console.log(` 该站点当月FCP总和: ${urlData.totals.fcp}`); console.log(` 该站点当月LCP总和: ${urlData.totals.lcp}`); }); console.log('---'); });
关键修正点
- 优先按月份分组:使用
item.fetchTime.substr(0,7)提取年份-月份作为分组键,确保同一月份的所有数据被合并 - 补充统计维度:新增月份总数据量、指标总和、平均值的计算,同时保留按URL分组的统计结果
- 兼容多指标场景:在双指标需求中,分别统计两个指标的总和与平均值,结构清晰且易于扩展
内容的提问来源于stack exchange,提问作者sathish kumar
相关产品推荐
相关产品推荐

