【问题标题】:What's a better algorithm to remove duplicates from an array of objects?从对象数组中删除重复项的更好算法是什么?
【发布时间】:2020-11-17 05:43:50
【问题描述】:

我有一个对象数组,看起来像(粗略的例子):

[{id:1, stuff:moreStuff}, {id:6, manyStuff,Stuffing}, {id:4, yayStuff, stuff}, {id:6, manyStuff, Stuffing}] 

问题是在数组中,有几个重复的对象。到目前为止,我想到的当前解决方案是这样的:

const DuplicateCheck = []
const FinalResult = []

for (let i = 0; i < ArrayOfObjects.length; i++) {
    let isPresent = false;
    for (let j = 0; j < duplicateCheck.length; j++) {
        if (ArrayOfObjects[i].id == duplicateCheck[j]) {
            isPresent = true;
        }
    }
    if (isPresent = false) {
        DuplicateCheck.push(ArrayOfObjects[i].id
        FinalResult.push(ArrayOfObjects[i]
    }
}

现在在学习了大 O 之后,这似乎是解决这个问题的一种非常低效的方法。所以我的问题是,有没有更好、更有效的方法来解决这个问题?

【问题讨论】:

  • 一种方法:排序到数组2,复制回来。如果数组已排序,则重复出现一个接一个,因此您只需要查看前一个值。 O ( n log n ) 排序 + O ( n ) 复制。如果你用谷歌搜索可能会有更好的方法,比如添加到哈希表并从中复制回来?
  • 如果要将对象映射到JSON 字符串,请从中创建一个新的Set,从集合中创建一个新的Array,然后将现在唯一的元素映射回对象, O 是什么?
  • 作为骗子,我没有因为结束问题而获得积分。你会知道,如果你阅读privileges 页面而不是带着你的偏见去。另外,你不是 OP @GirkovArpa,那你为什么还要回复呢?
  • @GirkovArpa:删除重复问题是一项社区服务,不会获得声望点或其他奖励。

标签: javascript arrays object duplicates big-o


【解决方案1】:

您可以将usedIds 保留为对象属性并仅在对象没有此类属性时添加到过滤后的数组中,或者如果您有可能,只需将您的项目添加到Set 中。设置为数据结构只能存储不重复。

无设置:

const filteredArray = [];
const usedIds = {};

for (const item of array) {
  if (!usedIds[item.id]) {
    usedIds[item.id] = true;
    filteredArray.push(item);
  }
}

有套装:

const filteredArray = [];
const usedIds = new Set();

for (const item of array) {
  if (!usedIds.has(item.id)) {
    usedIds.add(item.id);
    filteredArray.push(item);
  }
}

可运行示例:

const array = [
  {
    id: 1,
    stuff: 'stuff',
    moreStuff: 'moreStuff'
  },
  {
    id: 6,
    manyStuff: 'manyStuff',
    stuffing: 'stuffing'
  },
  {
    id: 4,
    yayStuff: 'yayStuff',
    stuff: 'stuff'
  },
  {
    id: 6,
    manyStuff: 'manyStuff',
    stuffing: 'stuffing'
  }
];

const filteredArray = [];
const usedIds = {};

for (const item of array) {
  if (!usedIds[item.id]) {
    usedIds[item.id] = true;
    filteredArray.push(item);
  }
}

console.log(filteredArray);

【讨论】:

    【解决方案2】:

    是的,为您的DuplicateCheck 使用Set,这样您就可以通过ID 访问O(1)

    const duplicateCheck = new Set
    const finalResult = []
    
    for (const object of arrayOfObjects) {
        if (!duplicateCheck.has(object.id)) {
            duplicateCheck.add(object.id)
            finalResult.push(object)
        }
    }
    

    【讨论】:

    • 不应该new Setnew Set() 吗?或者这些天括号是可选的?
    • @3limin4t0r 构造函数调用的括号have always been optional :-)
    【解决方案3】:

    您可以遍历数组并将id 存储在对象(哈希表)中,然后检查是否存在。比如:

    const DuplicateCheck = {}
    const FinalResult = []
    
    for (let i = 0; i < ArrayOfObjects.length; i++) {
        let currentId = ArrayOfObjects[i].id
    
        if (!DuplicateCheck[currentId]) {
            DuplicateCheck[currentId] = 1
            FinalResult.push(ArrayOfObjects[i])
        }
    }
    

    您将在 FinalResult 中收到所有唯一对象

    【讨论】:

      【解决方案4】:

      您还可以使用Map 过滤掉重复项。与Set approach by Bergi 相反,此解决方案保留了副本的最后一个版本,因为它使用相同的键覆盖键/值对。

      const objectsById = new Map(arrayOfObjects.map(object => [object.id, object]));
      const finalResult = Array.from(objectsById.values());
      

      上面的代码确实需要迭代集合 2 次。一次使用map 创建键/值对,一次将创建的数组转换为Map

      创建结果 objectsById 后,我们必须遍历这些值以将它们转换回数组。

      总的来说,这意味着对整个集合进行 2 到 3 次迭代,这通常不会比使用 find 的解决方案快很多。因为每次调用它都会遍历数组。

      如果省略map 调用并在objectsById 中手动插入元素,则可以将迭代次数减少1:

      const objectsById = new Map();
      for (const object of arrayOfObjects) {
        objectsById.set(object.id, object);
      }
      

      【讨论】:

        猜你喜欢
        • 2018-08-16
        • 2016-09-29
        • 2017-04-10
        • 2016-03-12
        • 2021-01-23
        相关资源
        最近更新 更多