获取对象数组中重复对象的列表

37

我正在尝试获取对象数组中的重复对象。假设对象如下所示。

values = [
  { id: 10, name: 'someName1' },
  { id: 10, name: 'someName2' },
  { id: 11, name: 'someName3' },
  { id: 12, name: 'someName4' }
];

重复的对象应该返回如下:

duplicate = [
  { id: 10, name: 'someName1' },
  { id: 10, name: 'someName2' }
];

2
你有什么问题? - ssc-hrep3
怎么尝试?你还没有展示任何尝试解决这个问题的代码。在网上搜索“数组重复项”应该会给你一个很好的起点。 - charlietfl
8个回答

64
你可以使用Array#reduce基于id键创建一个计数器查找表,然后使用Array#filter删除仅在查找表中出现一次的任何项。时间复杂度为O(n)。

const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];

const lookup = values.reduce((a, e) => {
  a[e.id] = ++a[e.id] || 0;
  return a;
}, {});

console.log(values.filter(e => lookup[e.id]));


2
你能解释一下你的代码吗?我有点困惑。你的代码中没有任何相等性判断,这怎么工作的?为什么每次迭代中的 a 都显示相同的对象?我需要对你的代码进行详细分析。 - Spectr
5
如果您还没有看过reducefilter的文档,我建议您去看一下。在这里,我使用reduce构建一个对象映射,将每个对象的id映射为其出现次数减1的计数。 a是累加器对象,通过reduce从一个回调传递到下一个回调。filter使用lookup[e.id]的真值来确定元素是否唯一。如果查找条目为0(假值),则它只被看到一次,并且filter将其从结果集中省略,否则它将保留。 - ggorlen
1
其实我知道reduce(不是专家但知道)和filter。也知道它们的工作原理。但是你的代码太难理解了。但是通过你的解释,一切都变得稍微容易了一点 :) 谢谢。 - Spectr
4
这是非常聪明的代码,我认为你的解释没有充分说明@ggorlen!为了确切地解释 reduce 内部发生了什么,这里是具体过程:每个数组项都会被箭头函数处理,可能创建查找表中的一个属性(新项)或增加计数器(重复项)。具体操作如下:a[e.id] = ++a[e.id] || 0 只会在该属性已经存在时才会使用 ++ 增加它。如果该属性不存在,则会通过 OR(||)运算符创建一个值为 0 的属性。 - hevans900
非常好的解决方案!谢谢 - dearwish
显示剩余4条评论

16

假设你有以下内容:

arr = [
    { id:10, name: 'someName1' },
    { id:10, name: 'someName2' },
    { id:11, name: 'someName3' },
    { id:12, name: 'someName4' }
]

因此,要获取唯一的项:

unique = arr
     .map(e => e['id'])
     .map((e, i, final) => final.indexOf(e) === i && i)
     .filter(obj=> arr[obj])
     .map(e => arr[e]);

然后,结果将会是

unique = [
     { id:10, name: 'someName1' },
     { id:11, name: 'someName3' },
     { id:12, name: 'someName4' }
]

而要获取重复的ID:

duplicateIds = arr
     .map(e => e['id'])
     .map((e, i, final) => final.indexOf(e) !== i && i)
     .filter(obj=> arr[obj])
     .map(e => arr[e]["id"])

将列出ID列表

duplicateIds = [10]

因此,要获取重复的对象:

duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));

现在你拥有它:

duplicate = [
    { id:10, name: 'someName1' },
    { id:10, name: 'someName2' }
]

感谢 https://reactgo.com/removeduplicateobjects/


我们如何找出多个属性的重复项? - Vivek
我不明白你的意思@Vivek,如果你能提供输入和期望输出的示例,那么我可以帮助! - HMagdy
2
你可以轻松地通过将“.map(e => e['id'])”替换为“.map(e => e['id']+'_'+e['name'])”来实现。 - HMagdy
1
map 中使用 indexOf 会使算法的时间复杂度变为 O(n^2)。使用上面提到的 countBygroupBy 方法更易于理解且时间复杂度为 O(n)。 - abhaga
1
你的找到唯一项的解决方案正是我们试图得出的,只需将indexOf更改为lastIndexOf即可实现我们所需的功能。谢谢! - Koshux
显示剩余2条评论

9
使用lodash,您可以通过filtercountBy解决这个问题,复杂度为O(n)

const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]

const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

你可以使用ES6做同样的事情,如下所示:

const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]

const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))


8

您并未明确说明具有不同id但相同“名称”的两个对象是否算作重复。 我将假设它们不算作重复; 换句话说,仅具有相同id的对象将被视为重复。

let ids = {};
let dups = [];

values.forEach((val)=> {
  if (ids[val.id]) {
    // we have already found this same id
    dups.push(val)
  } else {
    ids[val.id] = true;
  }
})
return dups;

4
您可以使用数组来存储唯一元素,并在其值上使用过滤器以仅返回重复项。
const unique = []

const duplicates = values.filter(o => {

   if(unique.find(i => i.id === o.id && i.name === o.name)) {
     return true
   }

   unique.push(o)
   return false;
})

2
使用lodash,您可以使用_.groupBy()按其id对元素进行分组。然后_.filter()筛选出成员少于两个的组,并_.flatten()结果:

const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];

const result = _.flow([
  arr => _.groupBy(arr, 'id'), // group elements by id
  g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
  _.flatten // flatten the results to a single array
])(values);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>


1
一种基于 @ggorlen 解决方案的替代方法,使用新的 Map() 作为累加器(以获得更好的性能),并且不使用一元运算符 ++ (在带有 ESLint 的项目中默认不建议使用)。

const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];

const lookup = values.reduce((a, e) => {
  a.set(e.id, (a.get(e.id) ?? 0) + 1);
  return a;
}, new Map());

console.log(values.filter(e => lookup.get(e.id) > 1));


-1

试试这个

function checkDuplicateInObject(propertyName, inputArray) {
  var seenDuplicate = false,
  testObject = {};

  inputArray.map(function(item) {
  var itemPropertyName = item[propertyName];    
  if (itemPropertyName in testObject) {
  testObject[itemPropertyName].duplicate = true;
  item.duplicate = true;
  seenDuplicate = true;
 }
 else {
   testObject[itemPropertyName] = item;
   delete item.duplicate;
 }
});

 return seenDuplicate;
}

引用自: http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/


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