在对象数组中创建精简集和嵌套数组

时间:2019-12-16 20:55:07

标签: javascript reduce

我正在努力将一系列“扁平”对象转换为压缩但嵌套的版本:

const startingArray = [
  { name: 'one', id: 100, thing: 1 },
  { name: 'one', id: 100, thing: 2 },
  { name: 'one', id: 100, thing: 4 },
  { name: 'two', id: 200, thing: 5 }
];

/*
desiredResult = [
  {name: 'one', id:100, things: [
    {thing: 1}, {thing: 2}, {thing:4}
  ]},
  {name: 'two', id:200, things: [
    {thing: 5}
  ]}
]
*/

// THIS DOES NOT WORK
const result = startingArray.reduce((acc, curr) => {
  if (acc.name) {
    acc.things.push(curr.thing)
  }
  return { name: curr.name, id: curr.id, things: [{thing: curr.thing}] };
}, {});

我不明白什么?!

2 个答案:

答案 0 :(得分:2)

在reduce回调中,acc不是“数组的每个元素”(curr就是)或“结果中的匹配元素”(您必须自己确定),这是每次调用该函数都会转换的累积对象。

也就是说,当您return { name: curr.name, id: curr.id, things: [{thing: curr.thing}] };时,它将acc设置为该对象以进行下一次迭代,并丢弃之前包含在其中的任何数据-acc.name将永远保存姓氏反复出现,结果永远不会累积到任何有意义的地方。

您想要做的是将结果累加到一个数组中(因为这是您想要的输出),请确保每次迭代都返回该数组:

const startingArray = [
  { name: 'one', id: 100, thing: 1 },
  { name: 'one', id: 100, thing: 2 },
  { name: 'one', id: 100, thing: 4 },
  { name: 'two', id: 200, thing: 5 }
];

const result = startingArray.reduce((acc, curr) => {
  let existing = acc.find(o => o.id == curr.id);
  if(!existing) acc.push(existing = { name: curr.name, id: curr.id, things: [] });
  existing.things.push({thing: curr.thing});
  return acc;
}, []);

console.log(result);

由于需要使用结果格式,因此涉及许多acc.find()调用,这很昂贵-您可以使用此技巧以简洁的方式解决此问题,将累加器数组的第一个元素用作引用的ID(还具有ES6解构功能):

const startingArray = [
  { name: 'one', id: 100, thing: 1 },
  { name: 'one', id: 100, thing: 2 },
  { name: 'one', id: 100, thing: 4 },
  { name: 'two', id: 200, thing: 5 }
];

const result = startingArray.reduce((acc, {name, id, thing}) => {
  if(!acc[0][id])
    acc.push(acc[0][id] = { name, id, things: [] });
  acc[0][id].things.push({thing});
  return acc;
}, [{}]).slice(1); //slice off the mapping as it's no longer needed

console.log(result);

答案 1 :(得分:1)

确定,提供其他方法。关键是您要累积一个对象,以后只取值,所以得到一个数组:

const startingArray = [
  { name: 'one', id: 100, thing: 1 },
  { name: 'one', id: 100, thing: 2 },
  { name: 'one', id: 100, thing: 4 },
  { name: 'two', id: 200, thing: 5 }
];

const res = startingArray.reduce((acc, curr) => {
   if (acc[curr.name]) {
     acc[curr.name].things.push({thing: curr.thing})
   } else {
     acc[curr.name] = { 
       name: curr.name,
       id: curr.id,
       things: [{thing: curr.thing}] 
    }
   }
   return acc
 }, {})

 console.log(Object.values(res))