如何检查对象数组中是否有重复的属性值?

144
我需要一些关于遍历数组的帮助,我总是卡住或者重复发明轮子。
values = [
    { name: 'someName1' },
    { name: 'someName2' },
    { name: 'someName1' },
    { name: 'someName1' }
]

我该如何检查数组中是否存在两个(或多个)相同的名称值?我不需要计数器,只需在数组值不唯一时设置一些变量。请注意,数组长度是动态的,数组值也是。


@AmiTavory 至少有一个明显的区别-那个问题看起来是针对原始数组(arr = [9, 9, 9, 111, 2, 3, 3, 3, 4, 4, 5, 7];),而这个问题则是基于对象属性进行去重。或许在语义上有些不同,但是之前最受欢迎的两个答案并没有完全解决这种情况。/giphy 让你更加了解(我知道这并不会做什么) - ruffin
16个回答

144

使用 Array.prototype.mapArray.prototype.some

var values = [
    { name: 'someName1' },
    { name: 'someName2' },
    { name: 'someName4' },
    { name: 'someName2' }
];

var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){ 
    return valueArr.indexOf(item) != idx 
});
console.log(isDuplicate);


5
如果数组很大,使用indexOf会导致非常糟糕的性能。 - thefourtheye
10
我会将返回部分替换为:return valueArr.indexOf(item, idx + 1) !== -1 - Orelsanpls
2
另一个选项可以是 new Set(arr).size !== arr.length 来检查数组是否有重复项。 - Joel Fernando
3
@JoelFernando - 这个答案是6年前写的,下面有人添加了一个使用Set的ES6答案。你的答案适用于原始类型的数组。这里的问题是关于一个对象数组,其中内部属性是我们不想重复的属性。 - Amir Popovich

91

ECMA Script 6 版本

如果您的环境支持 ECMA Script 6 的 Set,则可以使用 Array.prototype.some 和一个 Set 对象,就像这样:

let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
    return seen.size === seen.add(currentObject.name).size;
});

在这里,我们将每个对象的name插入到Set中,并检查添加之前和之后的size是否相同。这是有效的,因为Set.size基于唯一数据返回一个数字(只有当数据是唯一的时,set才会添加条目)。如果/当您有重复的名称时,大小不会增加(因为数据不会是唯一的),这意味着我们已经看到了当前的名称,并且它将返回true。

ECMAScript 5 版本

如果您不支持 Set,则可以像这样使用普通的 JavaScript 对象:

var seen = {};
var hasDuplicates = values.some(function(currentObject) {

    if (seen.hasOwnProperty(currentObject.name)) {
        // Current name is already seen
        return true;
    }

    // Current name is being seen for the first time
    return (seen[currentObject.name] = false);
});

同样的内容可以简洁地写成这样

var seen = {};
var hasDuplicates = values.some(function (currentObject) {
    return seen.hasOwnProperty(currentObject.name)
        || (seen[currentObject.name] = false);
});

注意: 在这两种情况下,我们使用 Array.prototype.some,因为它会进行短路运算。一旦函数返回真值,它将立即返回 true,并且不会处理其余的元素。


有趣的方法,使用hasOwnProperty。我在其他几个答案中看到你评论说,在大型数组中使用indexOf性能会很差。你建议的上述ES5方式是否更适用于大型对象的性能优化? - Josh Beam
@JoshBeam indexOf 会迭代数组以查找元素是否存在,但任何使用散列的东西都会更快。因此,如果数组很大,无论是 ES5 还是 ES6 的方式都会更快。 - thefourtheye
我该如何确保它会处理剩余的元素? - Anna Smother
如果您想处理所有元素,请使用 forEach 并自己编写逻辑。 - thefourtheye
1
@chovy 这个问题是:“如何检查数组中是否有两个(或更多)相同的名称值?我不需要计数器,只需要设置一些变量,如果数组值不唯一。”与删除重复项无关。我认为这是最好的答案! - calcazar
显示剩余3条评论

85

在 TS 和 ES6 中,您可以使用唯一属性创建一个新的 Set,并将其大小与原始数组进行比较。

const values = [
  { name: 'someName1' },
  { name: 'someName2' },
  { name: 'someName3' },
  { name: 'someName1' }
]

const uniqueValues = new Set(values.map(v => v.name));

if (uniqueValues.size < values.length) {
  console.log('duplicates found')
}


9
这实际上是最好的答案。 - arled
如果您计划将此转换为辅助函数,请注意,每当您使用不存在的属性进行检查,或者对象缺少该属性时,可能会得到错误的结果。例如,如果values中的最后两个对象缺少name属性,则values.map((v) => v.name)将返回['someName1', 'someName2', undefined, undefined],而hasDuplicates()将返回true,即使这实际上不是您想要的结果。因此,在使用正确的接口的TS中使用时效果最佳。 - thdoan

9
为了判断简单数组是否有重复,我们可以比较相同值的第一个最后一个索引:
该函数为:
var hasDupsSimple = function(array) {

    return array.some(function(value) {                            // .some will break as soon as duplicate found (no need to itterate over all array)
       return array.indexOf(value) !== array.lastIndexOf(value);   // comparing first and last indexes of the same value
    })
}

测试:

hasDupsSimple([1,2,3,4,2,7])
// => true

hasDupsSimple([1,2,3,4,8,7])
// => false

hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true

对于一个对象数组,我们需要先将对象的值转换为一个简单的数组:

使用map将对象数组转换为简单数组:

var hasDupsObjects = function(array) {

  return array.map(function(value) {
    return value.suit + value.rank

  }).some(function(value, index, array) { 
       return array.indexOf(value) !== array.lastIndexOf(value);  
     })
}

测试:

var cardHand = [
  { "suit":"spades", "rank":"ten" },
  { "suit":"diamonds", "rank":"ace" },
  { "suit":"hearts", "rank":"ten" },
  { "suit":"clubs", "rank":"two" },
  { "suit":"spades", "rank":"three" },
]

hasDupsObjects(cardHand);
// => false

var cardHand2 = [
  { "suit":"spades", "rank":"ten" },
  { "suit":"diamonds", "rank":"ace" },
  { "suit":"hearts", "rank":"ten" },
  { "suit":"clubs", "rank":"two" },
  { "suit":"spades", "rank":"ten" },
]

hasDupsObjects(cardHand2);
// => true

如何找到索引和最新的重复值 - paruvelly Vishwanath
@SystemsRebooter,谢谢!这正是我在寻找的。 - Appy Mango

8

const values = [
    { name: 'someName1' },
    { name: 'someName2' },
    { name: 'someName4' },
    { name: 'someName4' }
];

const foundDuplicateName = values.find((nnn, index) =>{
   return values.find((x, ind)=> x.name === nnn.name && index !== ind )
    })
    
    console.log(foundDuplicateName)

发现第一个重复的名称

const values = [
    { name: 'someName1' },
    { name: 'someName2' },
    { name: 'someName4' },
    { name: 'someName4' }
];

const foundDuplicateName = values.find((nnn, index) =>{
   return values.find((x, ind)=> x.name === nnn.name && index !== ind )
    })

7

如果你要查找一个布尔值,最快的方法是

var values = [
    { name: 'someName1' },
    { name: 'someName2' },
    { name: 'someName1' },
    { name: 'someName1' }
]

// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
  if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)


这可能是编写最快的方法,但考虑到您需要进行两次排序以及超出Array.map(),它绝对不是执行速度最快的方法。 - Neel Prajapati

5
你只需要一行代码。
var values = [
        { name: 'someName1' },
        { name: 'someName2' },
        { name: 'someName4' },
        { name: 'someName2' }
     ];
        let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;

3

尝试一个简单的循环:

var repeat = [], tmp, i = 0;

while(i < values.length){
  repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}

Demo


这最终只会得到一个去重后的数组。 - spartikus

2
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;

}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);

复制并粘贴以下代码以在 Node 中运行脚本:scriptname.js - ravi

2

使用Underscore.js 有几种使用Underscore的方法。这里是其中之一。检查数组是否已经唯一。

function isNameUnique(values){
    return _.uniq(values, function(v){ return v.name }).length == values.length
}

使用纯JavaScript:通过检查数组中是否有重复的名称来判断。

function isNameUnique(values){
    var names = values.map(function(v){ return v.name });
    return !names.some(function(v){ 
        return names.filter(function(w){ return w==v }).length>1 
    });
}

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