【问题标题】:Deduplicate array of objects by given property name通过给定的属性名称删除重复的对象数组
【发布时间】:2021-09-03 15:27:22
【问题描述】:

我有一个包含大约 1500 个元素的对象数组,我正在尝试创建一个新数组来删除具有重复唯一属性的元素。但是由于某种原因,当我运行该函数时,它会在数组的前 100 个元素处停止。我怎样才能让它遍历整个数组。

  const result = Array.from(new Set(DATA.map((a) => a.Numbers))).map(
    (Numbers) => {
      return DATA.find((a) => a.Numbers === Numbers);
    }
  );

【问题讨论】:

  • 只是一个旁注。从性能的角度来看,您的重复数据消除实施似乎不是很理想。
  • 感谢您的提醒,这里的新手还是会尝试其他实现。
  • 您确定有超过 100 个不同的 Numbers 属性吗?我看不出这段代码为什么会停止。
  • 是的,我很肯定我刚刚检查了我的 DATA 文件,即使最后一个元素行具有属性 Numbers。我无法绕过它。
  • 这是一个完全不同的问题。

标签: javascript arrays methods filter


【解决方案1】:

创建一个使用Numbers 属性作为键的对象。由于对象键必须是唯一的,这将删除重复项。然后获取对象值转换回数组。

const DATA = [{ Numbers: 1 },{ Numbers: 2 },{ Numbers: 3 },{ Numbers: 4 },{ Numbers: 1 },{ Numbers: 4 }];
const result = Object.values(Object.fromEntries(DATA.map(a => [a.Numbers, a])));
console.log(result)

【讨论】:

  • 请注意,此解决方案采用遇到的最后一个对象而不是第一个对象(重复)。根据您所处的场景,这可能完全没问题。
【解决方案2】:

你真的把事情复杂化了。您mapping 两次,将结果转换为一个集合,然后从该集合创建一个新数组。

使用简单的循环会更简单(也更易读),并记录对象中的数字。如果一个数字已经存在splice数组中的对象。

此方法不会创建 数组 - 您正在修改现有数组 - 但它会起作用。

const arr = [{ number: 1 },{ number: 2 },{ number: 3 },{ number: 4 },{ number: 1 },{ number: 4 }];

const numbers = new Set();

for (let i = arr.length - 1; i >= 0 ; i--) {
  const { number } = arr[i];
  if (numbers.has(number)) arr.splice(i, 1);
  numbers.add(number);
}

console.log(arr);

【讨论】:

  • tally 应该是 Set 以提高测试包容性。
  • 感谢@Barmar 的提醒。效率高多少?
  • 集合搜索是O(1),列表搜索是O(n)。
  • 啊,好的。谢谢。我从来没有真正理解过 O 数字。
  • 这些是最坏的情况。如果您使用数组并执行numbers.includes(number),在最坏的情况下includes 会迭代整个数组 O(n)。当您搜索的值是最后一个元素或不存在时,就会发生这种情况。集合被索引(类似于对象属性),因此numbers.has(number) 将“立即” O(1)(它仍然必须在某种搜索树中导航)知道值是否存在而不迭代整个集合。
【解决方案3】:

由于还没有基于Map 的答案(我相信Map 最适合from performance standpoint),我会发布我的:

const src = [{key: 'a', value: 1}, {key: 'c', value: 3}, {key: 'b', value: 2}, {key: 'a', value: 1}, {key: 'c', value: 3}]

const dedupe = (arr, keyProp) => [
    ...arr
        .reduce((acc, obj) => 
            (acc.set(obj[keyProp], obj), acc), new Map)
        .values()
]

const result = dedupe(src, 'key')

console.log(result)
.as-console-wrapper{min-height:100%;}

【讨论】:

  • 请注意,此解决方案采用遇到的最后一个对象而不是第一个对象(重复)。根据您所处的场景,这可能完全没问题。
  • @3limin4t0r :嗯,是的,这是基于具有相同目标道具值的项目具有相同其余道具的假设。否则,发布的解决方案都无效,因为 OP 和 Andy 的解决方案将只选择第一个匹配项(通过目标道具,忽略其余部分),而 Barmar 和我的将返回最后一个匹配项。
【解决方案4】:

制作不同对象数组的习惯用法(也在this answer 中描述)如下所示:

const distinct = DATA.filter((obj, idx) => 
  idx === data.findIndex(a => a.Numbers === obj.Numbers));

这会通过选择所有在线性搜索时返回与它们已有的相同索引的项目来过滤输入数组。从而根据给定的标准选择每个此类对象的第一个

注意:有些Numbers字符串,有些是实际的数字。 (以 0 开头的存储为字符串,例如 '02'。)如果您需要处理可能在字符串和数字中存储相同值的情况,您可以使用不太严格的 == 而不是 ===格式。例如:a.Numbers == obj.Numbers.

【讨论】:

  • 尽管非常简单,因此新手很容易理解,但如果您的输入数组足够大,您的解决方案显然实现了 O(n²) 时间算法,即terribly slow。这对于 OP 的用例来说可能完全没问题,但是值得一提。
猜你喜欢
  • 1970-01-01
  • 2020-11-03
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 1970-01-01
  • 2014-03-08
  • 2015-04-08
  • 1970-01-01
相关资源
最近更新 更多