使用 Array.reduce() 通过多个键对数组进行分组

Posted

技术标签:

【中文标题】使用 Array.reduce() 通过多个键对数组进行分组【英文标题】:Grouping an array by multiple keys with Array.reduce() 【发布时间】:2022-01-11 18:04:51 【问题描述】:

第一次写到这里,遇到如下问题。例如,我有以下数组:

const input = [
  
    id: 1,
    date: "01-01-2021",
    source: "TT",
    metrics:  conversions: 10, cpl: 3 ,
  ,
  
    id: 2,
    date: "01-01-2021",
    source: "TT",
    metrics:  conversions: 15, cpl: 3 ,
  ,
  
    id: 3,
    date: "01-01-2021",
    source: "FB",
    metrics:  conversions: 15, cpl: 4 ,
  ,
  
    id: 4,
    date: "01-01-2021",
    source: "FB",
    metrics:  conversions: 15, cpl: 4 ,
  ,
  
    id: 5,
    date: "02-01-2021",
    source: "TT",
    metrics:  conversions: 5, cpl: 2 ,
  ,
  
    id: 6,
    date: "02-01-2021",
    source: "TT",
    metrics:  conversions: 15, cpl: 2 ,
  ,
  
    id: 7,
    date: "02-01-2021",
    source: "FB",
    metrics:  conversions: 25, cpl: 1 ,
  ,
  
    id: 8,
    date: "02-01-2021",
    source: "FB",
    metrics:  conversions: 30, cpl: 1 ,
  ,
];

我需要得到一个按日期和来源分组的数组,其值将是特定日期的所有值的总和,例如如下结果:

const output = [
  
    date: "01-01-2021",
    TT: 25,
    FB: 30,
  ,
  
    date: "02-01-2021",
    TT: 20,
    FB: 55,
  ,
];

在我的解决方案中,我找到了唯一的键和日期,但不知道下一步该做什么。你能帮助我吗?示例代码:

const result = input.reduce((acc, current) => 
  const dateItem = acc.find((item) => item.date === current.date);
  const sources = input.map((item) => item.source).filter((value, index, self) => self.indexOf(value) === index);

  if (dateItem) 
    return [...acc.filter((i) => i.date !== current.date),  ...dateItem ];
  

  return [...acc,  date: current.date ];
, []);

【问题讨论】:

请解释一下将输入转化为输出的具体逻辑,这里不清楚。 【参考方案1】:

Array.reduceObject.values 将给出结果。

const input = [
   id: 1, date: "01-01-2021", source: "TT", metrics:  conversions: 10, cpl: 3 , ,
   id: 2, date: "01-01-2021", source: "TT", metrics:  conversions: 15, cpl: 3 , ,
   id: 3, date: "01-01-2021", source: "FB", metrics:  conversions: 15, cpl: 4 , ,
   id: 4, date: "01-01-2021", source: "FB", metrics:  conversions: 15, cpl: 4 , ,
   id: 5, date: "02-01-2021", source: "TT", metrics:  conversions: 5, cpl: 2 , ,
   id: 6, date: "02-01-2021", source: "TT", metrics:  conversions: 15, cpl: 2 , ,
   id: 7, date: "02-01-2021", source: "FB", metrics:  conversions: 25, cpl: 1 , ,
   id: 8, date: "02-01-2021", source: "FB", metrics:  conversions: 30, cpl: 1 , ,
];
const output = input.reduce((acc, curr) => 
  if (acc[curr.date]) 
    acc[curr.date][curr.source] = (acc[curr.date][curr.source] || 0) + curr.metrics.conversions; 
   else 
    acc[curr.date] = 
      date: curr.date,
      [curr.source]: curr.metrics.conversions
    
  
  return acc;
, );
console.log(Object.values(output));

【讨论】:

【参考方案2】:

使用日期作为键的简单化简循环。全部组合后,就可以使用值来获取数组了。

const input = [
    id: 1,
    date: "01-01-2021",
    source: "TT",
    metrics: 
      conversions: 10,
      cpl: 3
    ,
  ,
  
    id: 2,
    date: "01-01-2021",
    source: "TT",
    metrics: 
      conversions: 15,
      cpl: 3
    ,
  ,
  
    id: 3,
    date: "01-01-2021",
    source: "FB",
    metrics: 
      conversions: 15,
      cpl: 4
    ,
  ,
  
    id: 4,
    date: "01-01-2021",
    source: "FB",
    metrics: 
      conversions: 15,
      cpl: 4
    ,
  ,
  
    id: 5,
    date: "02-01-2021",
    source: "TT",
    metrics: 
      conversions: 5,
      cpl: 2
    ,
  ,
  
    id: 6,
    date: "02-01-2021",
    source: "TT",
    metrics: 
      conversions: 15,
      cpl: 2
    ,
  ,
  
    id: 7,
    date: "02-01-2021",
    source: "FB",
    metrics: 
      conversions: 25,
      cpl: 1
    ,
  ,
  
    id: 8,
    date: "02-01-2021",
    source: "FB",
    metrics: 
      conversions: 30,
      cpl: 1
    ,
  ,
];


const results = Object.values(input.reduce((o, data) => 
  o[data.date] = o[data.date] ||  date: data.date ;
  o[data.date][data.source] = (o[data.date][data.source] || 0) + data.metrics.conversions;
  return o;
, ));
console.log(results);

【讨论】:

非常感谢,我设法在您的解决方案中添加了日期属性,一切正常。对不起我的英语 应该Object.values(...)Object.entries(...).map( [date,vals] => (date,...vals) ) 哎呀漏掉了o[data.date] = o[data.date] || date: data.date ; 是的,我写了同一行。谢谢

以上是关于使用 Array.reduce() 通过多个键对数组进行分组的主要内容,如果未能解决你的问题,请参考以下文章

JMESPath 中的动态/计算键?

使用嵌套对象和键对数组建模

Array.reduce()方法的使用

javascript 使用array.reduce添加减号加数字

javascript 使用Array reduce可以安全地访问嵌套对象。

具有默认值的Array.reduce的CoffeeScript习惯用法