将对象数组转换为包含对象的数组的最佳方法

时间:2018-07-24 18:33:26

标签: javascript angular performance typescript for-loop

以这个极其简化的对象数组为例:

[
    {
      createID: '1'
      // Many other properties...
    },
    {
      createID: '1'
      // Many other properties...
    },
    {
      createID: '1'
      // Many other properties...
    },
    {
      createID: '37'
      // Many other properties...
    },
    {
      createID: '37'
      // Many other properties...
    },
    {
      createID: '2'
      // Many other properties...
    },
    {
      createID: '2'
      // Many other properties...
    },
    {
      createID: '14'
      // Many other properties...
    },
  ];

鉴于此数组,我然后使用对象createID属性创建一个包含对象[[{..},{..}], [{..}], ..n]的数组数组。我正在使用的当前前端框架(Angular v6)要求使用这种最终格式。

要完成此任务,我使用以下代码,其中tempArr是一个类似于上面提供的示例数组的数组。

    let currentGroup: string = tempArr[0].createID;
    let tempGrouped: any[] = [];
    let childGroup: any[] = [];
    tempArr.forEach(item => {
      if (item.createID !== currentGroup) {
        tempGrouped.push(childGroup);
        childGroup = [];
        currentGroup = item.createID;
      }
      childGroup.push(item);
    });
    tempGrouped.push(childGroup);

此代码可以正常工作。但是,我不禁相信在给定数据的基础上,必须有一种更有效,更优雅的方法将对象数组转换为包含对象的数组。

更新

请务必注意,createID只是ID表示应该将哪些对象分组在一起。因此,不需要createID对它们进行数字排序。另外,对象确实来自与它们的同级对象(与createID相同)的服务器“分组”,如在提供的给定示例数组中所见。

3 个答案:

答案 0 :(得分:1)

您的示例具有彼此相邻的所有相同ID。如果可以保证总是这样,则只需要遍历并推送到新数组即可。但是,如果不是这种情况,您的解决方案将无法正确分组项目。在这种情况下,使用哈希表将使您仍可以按渐近复杂度按ID分组。

您可以使用从createdID创建的键将对象分组为哈希表对象。这将使您有效地对所有内容进行分组。然后只需从哈希表中获取对象:

let arr = [{createID: '1'},{createID: '1'},{createID: '1'},{createID: '37'},{createID: '37'},{createID: '2'},{createID: '2'},{createID: '14'},];

let o = arr.reduce((a, c) => {
    (a[c.createID] || (a[c.createID] = [])).push(c)
    return a
}, {} )
// o is a an object with createID keys pointing to arrays of grouped objects
// just take the values
console.log(Object.values(o))

根据问题进行编辑

由于对象已经被分组,没有比循环遍历更好的方法了。如果您想要一个不添加临时数组的选项,则仍然可以使用reduce(),它与您当前的解决方案基本相同,但是可能更独立一些:

let tempArr = [{createID: '1'},{createID: '1'},{createID: '1'},{createID: '37'},{createID: '37'},{createID: '2'},{createID: '2'},{createID: '14'},];

let r = tempArr.reduce((a, c, i, self) => {
    if (i === 0 || self[i-1].createID !== c.createID) 
        a.push([])
    a[a.length - 1].push(c)
    return a
}, [])

console.log(r)

答案 1 :(得分:1)

假设您的数据数组存储在名为data的变量中:

  const result = data.reduce((acc, current) => {
    if (!acc.dictionary[current.createID]) {
      const createIdArray = [];
      acc.dictionary[current.createID] = createIdArray;
      acc.array.push(createIdArray);
    }

    acc.dictionary[current.createID].push(current);

    return acc;
  }, {array: [], dictionary: {}}).array;

这样,您将仅对数据循环一次,这非常有效,因为我们不使用过滤器或查找(这会一次又一次遍历整个数组)。

以下是输出:

[
  [
    {
      createID: '1',
    },
    {
      createID: '1',
    },
    {
      createID: '1',
    },
  ],
  [
    {
      createID: '37',
    },
    {
      createID: '37',
    },
  ],
  [
    {
      createID: '2',
    },
    {
      createID: '2',
    },
  ],
  [
    {
      createID: '14',
    },
  ],
];

这是一个正在运行的演示:https://stackblitz.com/edit/typescript-phbzug

摘要:

  • 字典是包含在reduce函数中的,这意味着reduce一旦完成,就会被垃圾回收
  • 不依赖任何外部变量,更易于推理和IMO更好的做法
  • 对于〜与OP答案相同的行数,此解决方案更健壮(不需要对数组进行排序)
  • 清洁:有了字典,您可以直接知道您要访问的内容,而且速度非常快

答案 2 :(得分:-1)

您要按createID分组吗?

let grouped=tempArray
//first get uniq values
.filter((s,index)=>tempArray.findIndex(f=>f.createID==s.createID)==index)
//then, with the uniq values make a map       
.map(seg=>{  //with each uniq value, create an object with two properties
   return {
      createID:seg.createID,  //the key
      items:tempArray.filter(s=>s.createID==seg.createID) //An array with the values
   }
})