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

905
我有一个包含对象数组的对象。
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"}

你的意思是如何阻止一个带有相同参数的散列表/对象被添加到数组中吗? - Matthew Lock
11
如果在数组中首次添加对象时防止重复会更简单,而不是之后再进行过滤,那么这也可以。 - Travis
3
即使是非常长的答案,但 MDN 可能有最短的答案:arrayWithNoDuplicates = Array.from(new Set(myArray)) - tonkatata
10
这在处理对象数组时无法正常运作。 - Debu Shinobi
你好,请查看下面一个简单且可重复使用的方法来管理重复项:https://stackoverflow.com/a/74544470/12930883 - RED-ONE
2
感谢@tonkatata的启发。可以使用Array.from(new Set(myArray.map(e => JSON.stringify(e)))))来创建对象数组。 - undefined
78个回答

3

我在这里使用reduce方法找到了一个简单的解决办法,可以从对象数组中删除重复项。我根据对象的position键过滤元素。

const med = [
  {name: 'name1', position: 'left'},
  {name: 'name2', position: 'right'},
  {name: 'name3', position: 'left'},
  {name: 'name4', position: 'right'},
  {name: 'name5', position: 'left'},
  {name: 'name6', position: 'left1'}
]

const arr = [];
med.reduce((acc, curr) => {
  if(acc.indexOf(curr.position) === -1) {
    acc.push(curr.position);
    arr.push(curr);
  }
  return acc;
}, [])

console.log(arr)


完美运行。 - dipenparmar12

2
const objectsMap = new Map();
const placesName = [
  { place: "here", name: "stuff" },
  { place: "there", name: "morestuff" },
  { place: "there", name: "morestuff" },
];
placesName.forEach((object) => {
  objectsMap.set(object.place, object);
});
console.log(objectsMap);

创建对象数组: console.log([...objectsMap.values()]); - Andriy Danylko

2

继续探索从对象数组中删除重复项的ES6方法:将Array.prototype.filterthisArg参数设置为new Set提供了一个不错的选择:

const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

const filtered = things.filter(function({place, name}) {

  const key =`${place}${name}`;

  return !this.has(key) && this.add(key);

}, new Set);

console.log(filtered);

然而,箭头函数() =>不能使用,因为this被绑定到它们的词法作用域。


2
 npm i lodash

 let non_duplicated_data = _.uniqBy(pendingDeposits, v => [v.stellarAccount, v.externalTransactionId].join());

2
问题可以简化为从“thing”数组中删除重复项。
您可以使用对象实现更快的O(n)解决方案(假设本地键查找可忽略),同时使用对象来维护唯一标准作为键并存储关联值。
基本上,这个想法是通过它们的唯一键存储所有对象,以便重复的对象会被覆盖:

const thing = [{ place: "here", name:"stuff" }, { place: "there", name:"morestuff" }, { place: "there", name:"morestuff" } ]

const uniques = {}
for (const t of thing) {
  const key = t.place + '$' + t.name  // Or whatever string criteria you want, which can be generified as Object.keys(t).join("$")
  uniques[key] = t                    // Last duplicate wins
}
const uniqueThing = Object.values(uniques)
console.log(uniqueThing)


2
你可以使用 SetFilter 方法来实现这个功能,

var arrObj = [{
  a: 1,
  b: 2
}, {
  a: 1,
  b: 1
}, {
  a: 1,
  b: 2
}];

var duplicateRemover = new Set();

var distinctArrObj = arrObj.filter((obj) => {
  if (duplicateRemover.has(JSON.stringify(obj))) return false;
  duplicateRemover.add(JSON.stringify(obj));
  return true;
});

console.log(distinctArrObj);

Set 是一种独特的原始类型集合,因此不能直接在对象上使用,但是 JSON.stringify 将其转换为原始类型即 String,因此我们可以进行过滤。

如果您想基于某个特定键(例如 key)删除重复项,则可以使用 obj.key 替换 JSON.stringify(obj)


2

一行代码实现es6魔法...而且易读!

// returns the union of two arrays where duplicate objects with the same 'prop' are removed
const removeDuplicatesWith = (a, b, prop) => {
  a.filter(x => !b.find(y => x[prop] === y[prop]));
};

这个问题有两个方面的不可行之处。不仅原始解决方案被两个人通过添加大括号但在a.filter之前没有添加返回语句而从根本上改变了,而且原始函数本身也无法正常工作,因为它忽略了第二个数组中不在第一个数组中的任何项。 - MarkC

2

来源

JSFiddle

这将删除重复的对象,而不需要传递任何键。

uniqueArray = a => [...new Set(a.map(o => JSON.stringify(o)))].map(s => JSON.parse(s));

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

var unique = uniqueArray(objects);
console.log('Original Object',objects);
console.log('Unique',unique);

uniqueArray = a => [...new Set(a.map(o => JSON.stringify(o)))].map(s => JSON.parse(s));

    var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

    var unique = uniqueArray(objects);
    console.log(objects);
    console.log(unique);

你能解释一下正在发生的事情以及它是如何工作的吗? - MillerC
1
我尝试了一下,从我的观察来看,[...new Set(a.map(o => JSON.stringify(o)))]返回的是去重后的值,因为新的Set会移除重复项。Set必须将(o)字符串化,以便正确地删除重复的“字符串”,然后以下的.map将字符串化的值转换回对象。 - MillerC

2

如果您严格希望基于一个属性删除重复项,则可以根据place属性将数组reduce为对象,因为对象只能具有唯一的键,然后只需获取values即可返回到数组:

最初的回答

const unique = Object.values(things.thing.reduce((o, t) => ({ ...o, [t.place]: t }), {}))

2

我认为结合reduceJSON.stringify对对象进行比较,并选择性地将不在累加器中的对象添加进去是一种优雅的方式。

请注意,当数组中有许多复杂对象时,JSON.stringify可能会成为性能问题,但在大部分情况下,这是最简短的方法,我的意见是如此。

var collection= [{a:1},{a:2},{a:1},{a:3}]

var filtered = collection.reduce((filtered, item) => {
  if( !filtered.some(filteredItem => JSON.stringify(filteredItem) == JSON.stringify(item)) )
    filtered.push(item)
  return filtered
}, [])

console.log(filtered)

另一种写法(但效率较低):

collection.reduce((filtered, item) => 
  filtered.some(filteredItem => 
    JSON.stringify(filteredItem ) == JSON.stringify(item)) 
      ? filtered
      : [...filtered, item]
, [])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接