按日期将对象数组分组到年和月

Group Array of Objects to Year and Month by Date

给定以下对象数组:

[{
    "id": 1,
    "name": "random_name1",
    "published_at": "2021-01-16T08:52:24.408Z",
},
{
    "id": 2,
    "name": "random_name2",
    "published_at": "2022-02-16T08:52:24.408Z",
},
{
    "id": 3,
    "name": "random_name3",
    "published_at": "2020-04-16T08:52:24.408Z",
},
{
    "id": 4,
    "name": "random_name4",
    "published_at": "2020-04-16T08:52:24.408Z",
},
{
    "id": 5,
    "name": "random_name5",
    "published_at": "2022-05-16T08:52:24.408Z",
}
]

我需要按年份和月份对嵌套对象(降序)的 一个数组 中的项目进行分组,结果应为:

 [
  {
    year: '2022',
    months: [
      {
        month: '5',
        items: [
          {
            id: '5',
            name: 'random_name5'
          }
        ]
      },
      {
        month: '2',
        items: [
          {
            id: '2',
            name: 'random_name2'
          }
        ]
      }
    ]
  },
  {
    year: '2021',
    months: [
      {
        month: '1',
        items: [
          {
            id: '1',
            name: 'random_name1'
          }
        ]
      },
      {
        month: '2',
        items: [
          {
            id: '2',
            name: 'random_name2'
          }
        ]
      }
    ]
  },
  {
    year: '2020',
    months: [
      {
        month: '4',
        items: [
          {
            id: '3',
            name: 'random_name3'
          },
          {
            id: '4',
            name: 'random_name4'
          }
        ]
      }
    ]
  }
];

我尝试了以下方法:

items = [...new Set(items.map((item) => parseInt(item.published_at.split('-')[0])))].map((year) => [
  {
    year: year,
    months: [
      ...new Set(
        items
          .filter((item) => parseInt(item.published_at.split('-')[0]) === year)
          .map((item) => parseInt(item.published_at.split('-')[1]))
      )
    ].map((month) => [
      {
        month: month,
        items: items.filter(
          (item) => parseInt(item.published_at.split('-')[0]) === year && parseInt(item.published_at.split('-')[1]) === month
        )
      }
    ])
  }
]);

return items

上述解决方案的问题在于,它会像这样创建一个二维数组(月份也是二维的):

[
  [ { year: 2022, months: [Array] } ],
  [ { year: 2021, months: [Array] } ],
  [ { year: 2020, months: [Array] } ],
  [ { year: 2019, months: [Array] } ],
  [ { year: 2018, months: [Array] } ]
]

如何解决这个问题?

给定你的数组 data,你可以用数组方法做一些事情,比如 mapreduce.

像这样:

const groupedByYear = data.map((e) => ({ ...e, published_at: new Date(e.published_at) }))
.reduce((acc, e) => {
  const year = e.published_at.getFullYear();
  const month = e.published_at.getMonth() + 1;
  if (!acc[year]) acc[year] = { year };
  if (!acc[year][month]) acc[year][month] = [];
  acc[year][month] = e;
  return acc;
}, {})

const result = Object.values(groupedByYear).reduce((acc, e) => {
  const { year, ...months } = e;
  acc.push({ year: year, months: months });
  return acc;
}, [])

这是一个示例,可能不是执行此操作的最佳方法。它只是为了向您展示数据转换的路径。

首先data.map能够对日期进行操作。然后 reduce 对数据进行分组(这里使用一个对象)。然后从对象值创建一个数组以匹配您想要的输出。

与您展示的解决方案相比,优点是可以限制迭代数组的次数。避免在数组上迭代太多时间以获得更好的性能始终是个好主意。

如果您获得 year-months 的唯一列表,您可以使用它来映射您的对象

const items = [{ "id": 1,"name": "random_name1","published_at": "2021-01-16T08:52:24.408Z", },
{ "id": 2, "name": "random_name2", "published_at": "2022-02-16T08:52:24.408Z",},
{ "id": 3, "name": "random_name3","published_at": "2020-04-16T08:52:24.408Z",},
{"id": 4, "name": "random_name4", "published_at": "2020-04-16T08:52:24.408Z",},
{ "id": 5, "name": "random_name5", "published_at": "2022-05-16T08:52:24.408Z",}]
let uniqueYearMonths = [... new Set(items.map(x => x.published_at.substring(0,7)))];
let results = [... new Set(items.map(x => x.published_at.substring(0,4)))]
  .map(year => ({
    year: year,
    months: uniqueYearMonths
      .filter(ym => ym.startsWith(year))
      .map(ym => ({ 
        month: ym.substring(5,7),
        items: items
          .filter(item => item.published_at.startsWith(ym))
          .map(item => ({
            id: item.id,
            name: item.name
          }))
      }))
    }));

console.log(results);