【问题标题】:How to filter an array of objects by dates and values如何按日期和值过滤对象数组
【发布时间】:2022-01-10 17:41:51
【问题描述】:

我得到的是一个包含类别、日期和值的对象数组,就像这样

const records = 
  [ { category: 'Cat 1', date: '2020-11-03', value: '300.00' } 
  , { category: 'Cat 2', date: '2020-11-03', value: '350.00' } 
  , { category: 'Cat 3', date: '2020-11-03', value:  '50.00' } 
  , { category: 'Cat 1', date: '2020-11-13', value: '200.00' } 
  , { category: 'Cat 1', date: '2020-12-23', value: '100.00' } 
  , { category: 'Cat 2', date: '2020-11-23', value: '350.00' } 
  , { category: 'Cat 3', date: '2020-11-15', value:  '50.00' } 
  , { category: 'Cat 3', date: '2021-01-15', value:  '50.00' } 
  ]

我想要得到的是每个类别、月份和每月总价值的新数组,像这样:

const newRecords = 
  [ { category: 'Cat 1'
    , totalPerMonth: 
      [ { month: '2020-11', totalMonthlyValue: '500.00' } 
      , { month: '2020-12', totalMonthlyValue: '100.00' } 
    ] } 
  , { category: 'Cat 2'
    , totalPerMonth: 
      [ { month: '2020-11', totalMonthlyValue: '700.00' } 
    ] } 
  , { category: 'Cat 3'
    , totalPerMonth: 
      [ { month: '2020-11', totalMonthlyValue: '100.00' } 
      , { month: '2021-01', totalMonthlyValue:  '50.00' } 
  ] } ]

【问题讨论】:

  • 如果 newRecord 是一个以 'Cat 1','Cat 2' 为字段的对象会不会更容易

标签: javascript arrays sorting filtering


【解决方案1】:

这边

const records = 
  [ { category: 'Cat 1', date: '2020-11-03', value: '300.00' } 
  , { category: 'Cat 2', date: '2020-11-03', value: '350.00' } 
  , { category: 'Cat 3', date: '2020-11-03', value:  '50.00' } 
  , { category: 'Cat 1', date: '2020-11-13', value: '200.00' } 
  , { category: 'Cat 1', date: '2020-12-23', value: '100.00' } 
  , { category: 'Cat 2', date: '2020-11-23', value: '350.00' } 
  , { category: 'Cat 3', date: '2020-11-15', value:  '50.00' } 
  , { category: 'Cat 3', date: '2021-01-15', value:  '50.00' } 
  ]

const result =
  Object.values(
  records.reduce((r,{category,date,value}) =>
    {
    let Ym  = date.substring(0,7)
    if (!r[category])          r[category] = { category, sum : {} }
    if (!r[category].sum[Ym])  r[category].sum[Ym] = 0
    r[category].sum[Ym] += Number(value)
    return r
    },{}
  )).map(({category,sum}) =>
    {
    let totalPerMonth = Object.entries(sum).map(([month,sum])=>({month,totalMonthlyValue:sum.toFixed(2)}))
    return { category,totalPerMonth }
    },{})

console.log ( result  )
.as-console-wrapper {max-height: 100%!important;top:0 }

【讨论】:

    【解决方案2】:

    您可以先按类别再按日期对数据进行分组。然后,您需要将所有值相加并返回定义的结构。这是一个正是这样做的例子。

    const records = [
      {category: 'Cat 1', date: '2020-11-03', value: '300.00'},
      {category: 'Cat 2', date: '2020-11-03', value: '350.00'},
      {category: 'Cat 3', date: '2020-11-03', value: '50.00'},
      {category: 'Cat 1', date: '2020-11-13', value: '200.00'},
      {category: 'Cat 1', date: '2020-12-23', value: '100.00'},
      {category: 'Cat 2', date: '2020-11-23', value: '350.00'},
      {category: 'Cat 3', date: '2020-11-15', value: '50.00'},
      {category: 'Cat 3', date: '2021-01-15', value: '50.00'}
    ];
    
    // first you need to group it by category, you can do that by
    // assigning a group indicator as key to an object and add an
    // array as value containing all values. you can also use lodash's
    // groupBy for grouping values.
    const groupedRecords = {};
    records.forEach(record => {
      if (groupedRecords[record.category] === undefined) {
        groupedRecords[record.category] = [];
      }
      groupedRecords[record.category].push(record);
    });
    
    // then, you need to go through each category group and group all
    // records again by date. you can use the same pattern as above.
    const result = Object.entries(groupedRecords).map(([category, recordsOfOneCategory]) => {
      const groupedDates = {};
      recordsOfOneCategory.forEach(record => {
        // this regex extracts the date per month. it's probably better 
        // to use a date library for this part
        const month = record.date.match(/^(\d{4}-\d{2})/)[1];
        if (groupedDates[month] === undefined) {
          groupedDates[month] = [];
        }
        groupedDates[month].push(record);
      });
      
      // now you have for each category group a group of dates with
      // records in it. you now need to make a sum for each group of
      // dates. you can do this by mapping the values into an array of
      // only values and then sum them up using reduce.
      const totalPerMonth = Object.entries(groupedDates).map(([month, items]) => ({
        month,
        totalMonthlyValue: items.map(item => parseFloat(item.value)).reduce((a,b) => a + b).toFixed(2)
      }));
      
      return { category, totalPerMonth };
    });
    
    console.log(result);

    【讨论】:

    • 出色的解决方案,也得到了很好的解释。非常感谢您的时间和精力
    【解决方案3】:

    您可以在对象中使用array#reduce 根据类别和月份进行分组。然后使用Object.values()array#map 从这个对象中提取所有值。

    const records = [ { category: 'Cat 1', date: '2020-11-03', value: '300.00' } , { category: 'Cat 2', date: '2020-11-03', value: '350.00' } , { category: 'Cat 3', date: '2020-11-03', value: '50.00' } , { category: 'Cat 1', date: '2020-11-13', value: '200.00' } , { category: 'Cat 1', date: '2020-12-23', value: '100.00' } , { category: 'Cat 2', date: '2020-11-23', value: '350.00' } , { category: 'Cat 3', date: '2020-11-15', value: '50.00' } , { category: 'Cat 3', date: '2021-01-15', value: '50.00' } ],
          result = records.reduce((r, {category, date, value}) => {
            const month = date.substring(0, 7);
            r[category] ??= {category};
            r[category][month] ??= {month, total: 0};
            r[category][month].total += +value;
            return r;
          },{}),
          newRecords = Object
            .values(result)
            .map( ({category, ...rest}) => 
              ({  category, 
                  totalPerMonth: Object.values(rest).map(({ month, total }) => 
                                  ({ month, totalMonthlyValue: total.toFixed(2) }) 
                                  ) }) );
    console.log(newRecords);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 你所有的totalMonthlyValue都错了
    猜你喜欢
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多