如何将年度产品销售数组按客户、产品分组为月度字段结构
问题:将客户月度销售数据整理为按客户-产品分组的年度记录
问题背景
我有一个存储了多位客户年度各月不同产品销售数据的数组,希望将每位客户的年度采购数据整理为单条记录结构。
示例数据
var sales = [ ["Jan", "Customer 1", "Product 1", 538], ["Feb", "Customer 1", "Product 1", 0], ["March", "Customer 1", "Product 1", 252], ["Apr", "Customer 1", "Product 1", 0], ["May", "Customer 1", "Product 1", 630], ["June", "Customer 1", "Product 1", 945], ["July", "Customer 1", "Product 1", 0], ["Aug", "Customer 1", "Product 1", 630], ["Sept", "Customer 1", "Product 1", 756], ["Oct", "Customer 1", "Product 1", 0], ["Nov", "Customer 1", "Product 1", 0], ["Dec", "Customer 1", "Product 1", 630], ["Jan", "Customer 1", "Product 2", 50], ["Feb", "Customer 1", "Product 2", 630], ... ]
已尝试的代码
我已尝试获取唯一产品列表和唯一客户列表,但不知后续如何处理:
// 获取唯一产品列表 var products = sales.map(item => { return item[2]; }).filter((val, index, self) => { return self.indexOf(val) === index; }); console.log(products); // 获取唯一客户列表 var targets = sales.map(item => { return item[1] }).filter((val, index, self) => { return self.indexOf(val) === index }); console.log(targets);
期望结果
我期望得到按客户、产品分组的对象数组,格式如下:
[ { "customer": "Customer 1", "product": "Product 1", "jan": 538, "feb": 0, "march": 252, ... }, { "customer": "Customer 1", "product": "Product 2", "jan": 50, "feb": 630, ... } ]
实现思路与代码
其实核心思路很简单:按「客户+产品」的组合作为唯一标识来分组,把每个月的销售额数据挂载到对应的分组对象里就行。不用先单独提取客户和产品列表再嵌套循环,这样效率更高,只需要遍历原始数组一次就能搞定。
具体步骤
- 先创建一个临时对象(比如叫
groupedData),用来暂存每个客户+产品的组合数据,用客户名_产品名作为键,避免重复分组; - 遍历原始的
sales数组,对每一条数据做如下处理:- 提取出月份、客户、产品、销售额这四个值;
- 把月份转成小写(和你期望结果里的键名保持一致);
- 生成唯一的分组键,比如
${customer}_${product}; - 如果这个分组键在临时对象里不存在,就先初始化一个包含
customer和product属性的基础对象; - 把当前月份的销售额添加到这个基础对象里,键就是小写的月份名;
- 遍历完成后,用
Object.values(groupedData)把临时对象里的所有值提取出来,就是你要的数组格式了。
完整可运行代码
var sales = [ ["Jan", "Customer 1", "Product 1", 538], ["Feb", "Customer 1", "Product 1", 0], ["March", "Customer 1", "Product 1", 252], ["Apr", "Customer 1", "Product 1", 0], ["May", "Customer 1", "Product 1", 630], ["June", "Customer 1", "Product 1", 945], ["July", "Customer 1", "Product 1", 0], ["Aug", "Customer 1", "Product 1", 630], ["Sept", "Customer 1", "Product 1", 756], ["Oct", "Customer 1", "Product 1", 0], ["Nov", "Customer 1", "Product 1", 0], ["Dec", "Customer 1", "Product 1", 630], ["Jan", "Customer 1", "Product 2", 50], ["Feb", "Customer 1", "Product 2", 630] ]; function groupSalesData(sales) { const groupedData = {}; sales.forEach(item => { // 解构提取数据,也可以用item[0], item[1]这种方式,看你习惯 const [month, customer, product, amount] = item; // 转成小写的月份键 const monthKey = month.toLowerCase(); // 生成唯一分组标识 const groupKey = `${customer}_${product}`; // 初始化分组对象(如果不存在的话) if (!groupedData[groupKey]) { groupedData[groupKey] = { customer: customer, product: product }; } // 挂载当月销售额 groupedData[groupKey][monthKey] = amount; }); // 把对象转换为数组返回 return Object.values(groupedData); } // 调用函数并查看结果 const formattedSales = groupSalesData(sales); console.log(formattedSales);
小补充
- 如果你的客户名或产品名里可能包含下划线,可以换用其他分隔符,比如
|,只要能保证「客户+分隔符+产品」的组合唯一就没问题; - 要是需要兼容ES5及以下的旧环境,只需要把解构赋值改成普通的变量提取:
const month = item[0]; const customer = item[1]; ...就行,其他逻辑完全不变。
内容的提问来源于stack exchange,提问作者P.Gichia
相关产品推荐
相关产品推荐

