如何从对象数组中删除所有重复项?

IT技术 javascript arrays object duplicates
2020-12-17 16:02:08

我有一个包含对象数组的对象。

obj = {};

obj.arr = new Array();

obj.arr.push({place:"here",name:"stuff"});
obj.arr.push({place:"there",name:"morestuff"});
obj.arr.push({place:"there",name:"morestuff"});

我想知道从数组中删除重复对象的最佳方法是什么。因此,例如,obj.arr将成为...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
6个回答

来点es6魔法怎么样?

things.thing = things.thing.filter((thing, index, self) =>
  index === self.findIndex((t) => (
    t.place === thing.place && t.name === thing.name
  ))
)

参考网址

更通用的解决方案是:

const uniqueArray = things.thing.filter((thing, index) => {
  const _thing = JSON.stringify(thing);
  return index === things.thing.findIndex(obj => {
    return JSON.stringify(obj) === _thing;
  });
});

使用上述属性策略而不是JSON.stringify

const isPropValuesEqual = (subject, target, propNames) =>
  propNames.every(propName => subject[propName] === target[propName]);

const getUniqueItemsByProperties = (items, propNames) => 
  items.filter((item, index, array) =>
    index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNames))
  );

如果您希望propNames属性是数组或值,则可以添加包装器

const getUniqueItemsByProperties = (items, propNames) => {
  const propNamesArray = Array.from(propNames);

  return items.filter((item, index, array) =>
    index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNamesArray))
  );
};

允许getUniqueItemsByProperties('a')getUniqueItemsByProperties(['a']);

Stackblitz 示例

解释

  • 首先了解所使用的两种方法:
  • 接下来想想是什么让你的两个对象相等,并记住这一点。
  • 我们可以将某物检测为重复,如果它满足我们刚刚想到的标准,但它的位置不在具有该标准的对象的第一个实例上。
  • 因此,我们可以使用上述标准来确定某些内容是否重复。
一个问题,这不是 O(n^2) 的方法。如果我处理 30 条记录,我将进行 900 次迭代,对吗?(最坏的情况,没有重复)
2021-02-18 16:02:08
@vsync 只需将@BKM 的答案放在一起,一个通用的解决方案是:const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28
2021-02-19 16:02:08
如果您有一个包含 200,000 个条目的数组,那么这将需要 400 亿次迭代。这不应该与大型数组一起使用。始终使用地图代替。
2021-02-20 16:02:08
这里的关键是 findIndex() 方法返回第一个元素的索引,因此如果有第二个元素匹配,则在过滤过程中永远不会找到并添加它。我盯着它看了一分钟:)
2021-02-22 16:02:08
这可以缩短为: things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
2021-03-05 16:02:08

一个带 Map 的衬垫(高性能,不保留顺序)

id在数组中查找 unique

[...new Map(arr.map(v => [v.id, v])).values()]

多个属性唯一(placename

[...new Map(arr.map(v => [JSON.stringify([v.place,v.name]), v])).values()]

所有属性都独一无二

[...new Map(arr.map(v => [JSON.stringify(v), v])).values()]

保持第一次出现。

[...new Map(arr.slice().reverse().map(v => [v.id, v])).values()].reverse()

带过滤器(保留顺序,低性能)

id在数组中查找 unique

arr.filter((v,i,a)=>a.findIndex(t=>(t.id===v.id))===i)

多个属性唯一(placename

arr.filter((v,i,a)=>a.findIndex(t=>(t.place === v.place && t.name===v.name))===i)

所有属性都是唯一的(这对于大型数组来说会很慢)

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)

保留最后一次出现。

arr.slice().reverse().filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i).reverse()
什么样tfindIndex立场呢?
2021-02-14 16:02:08
这对于查找我的 vue 模式中的键值对是否有重复非常有效。+1
2021-02-16 16:02:08
简单的美丽
2021-02-28 16:02:08
v,i,a == 值、索引、数组
2021-03-05 16:02:08
arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i) 如果有键,这将不起作用顺序不同
2021-03-08 16:02:08

一种原始方法是:

const obj = {};

for (let i = 0, len = things.thing.length; i < len; i++) {
  obj[things.thing[i]['place']] = things.thing[i];
}

things.thing = new Array();

 for (const key in obj) { 
   things.thing.push(obj[key]);
}
你永远不应该在 for 循环中使用长度,因为它会减慢每次迭代计算它的速度。将其分配给循环外的变量并传递变量而不是 things.thing.length。
2021-02-12 16:02:08
如何修改上述内容以从包含 X 和重复数据删除的数组中删除对象?
2021-02-14 16:02:08
@aefxx 我不太明白这个功能,你如何处理“地方”相同但名称不同的情况,是否应该考虑dup?
2021-02-27 16:02:08
@DeepakGM 你说得对。答案不会(必然)保留给定的顺序。如果这是一项要求,则应寻找另一种解决方案。
2021-03-01 16:02:08
虽然这有效,但它不处理排序数组,因为从不保证获取键的顺序。因此,您最终再次对其进行排序。现在,假设数组没有排序但它的顺序很重要,你无法确保顺序保持不变
2021-03-09 16:02:08

在一行中使用ES6+可以通过键获得唯一的对象列表:

const unique = [...new Map(arr.map(item => [item[key], item])).values()]

它可以放入一个函数中:

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

这是一个工作示例:

const arr = [
    {place: "here",  name: "x", other: "other stuff1" },
    {place: "there", name: "x", other: "other stuff2" },
    {place: "here",  name: "y", other: "other stuff4" },
    {place: "here",  name: "z", other: "other stuff5" }
]

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

const arr1 = getUniqueListBy(arr, 'place')

console.log("Unique by place")
console.log(JSON.stringify(arr1))

console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')

console.log(JSON.stringify(arr2))

它是如何工作的

首先,数组以一种可以用作Map输入的方式重新映射

arr.map(item => [item[key], item]);

这意味着数组的每一项都将转换为另一个包含 2 个元素的数组;所述选择的键作为第一元件和整个初始项目作为第二元件,这被称为一个条目(例如,阵列的条目映射条目)。这里是官方的文档与演示如何在地图构造函数到数组项的例子。

关键是place 的示例

[["here", {place: "here",  name: "x", other: "other stuff1" }], ...]

其次,我们将这个修改过的数组传递给 Map 构造函数,这就是神奇的发生。Map 将消除重复的键值,只保留同一键的最后插入值。 注意:Map 保持插入的顺序。检查地图和对象之间的区别

新地图(上面刚刚映射的条目数组)

第三,我们使用地图值来检索原始项目,但这次没有重复项。

新地图(mappedArr).values()

最后一个是将这些值添加到一个新的数组中,这样它就可以作为初始结构并返回:

返回 [...new Map(mappedArr).values()]

你的 ES6 函数看起来非常简洁实用。你能再解释一下吗?究竟发生了什么?是否删除了第一个或最后一个重复项?或者它是随机的,哪个重复被删除了?那会很有帮助,谢谢。
2021-02-16 16:02:08
据我所知,创建了一个以属性值为键的 Map。但是,如何或是否保留数组的顺序并不是 100%。
2021-02-20 16:02:08
嗨@DavidSchumann,我会更新答案并解释它是如何工作的。但对于简短的回答,订单被保留,第一个被删除......想想它是如何插入到地图中的......它检查密钥是否已经存在它会更新它,因此最后一个将保留
2021-02-24 16:02:08
这不能回答原始问题,因为这是搜索id. 该问题需要整个对象在所有领域都是唯一的,例如placename
2021-02-25 16:02:08
如果项目为空或某些项目没有被调用的密钥,则会出现错误,对此有何解决方法?
2021-02-28 16:02:08

如果您可以使用下划线或 lodash等 Javascript 库,我建议您查看_.uniq它们库中的函数。来自lodash

_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])

基本上,您传入这里是对象文字的数组,然后传入要删除原始数据数组中重复项的属性,如下所示:

var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name'); 

更新:Lodash 现在也引入了.uniqBy

@Praveen Pds:我在代码示例中是否说过关于下划线的内容?我说 'lodash' 有这个功能,下划线也有类似的功能。在投票之前,请仔细阅读答案。
2021-02-10 16:02:08
注意:您现在需要使用uniqBy而不是uniq,例如_.uniqBy(data, 'name')...文档:lodash.com/docs#uniqBy
2021-02-16 16:02:08
//使用 _underscore.js 列出唯一的对象 holderObject = _.uniq(holdingObject , function(item, key, name) { return item.name; });
2021-03-06 16:02:08