检查数组中是否包含重复的值

43

我想编写一个 JavaScript 函数,用于检查数组是否包含重复的值。

我已经编写了以下代码,但它始终返回 "true"。

请问有谁能告诉我我漏掉了什么?

function checkIfArrayIsUnique(myArray) 
    {
        for (var i = 0; i < myArray.length; i++) 
        {
            for (var j = 0; j < myArray.length; j++) 
            {
                if (i != j) 
                {
                    if (myArray[i] == myArray[j]) 
                    {
                        return true; // means there are duplicate values
                    }
                }
            }
        }
        return false; // means there are no duplicate values.
    }

3
“array is unique” 的意思是“数组是唯一的”。 - deceze
1
你想检查数组中的每个值是否唯一(检查重复项),还是将该数组与另一个数组进行比较以确定其是否唯一? - David Thomas
checkIfArrayIsUnique([1,2,3]),它是假的伙计,你的函数在工作。 - Sarath
不良的命名。"Check if unique" → "True" 意味着 "存在重复项" ... - deceze
1
你还可以将第二个循环改为 for (var j = i; j < myArray.length; j++),因为你已经检查过前面的指数了,所以没有必要再次比较它们的唯一性。 - Richard Dalton
显示剩余2条评论
17个回答

117

如果您使用ES6,一个简单的解决方案是使用Set

function checkIfArrayIsUnique(myArray) {
  return myArray.length === new Set(myArray).size;
}

let uniqueArray = [1, 2, 3, 4, 5];
console.log(`${uniqueArray} is unique : ${checkIfArrayIsUnique(uniqueArray)}`);

let nonUniqueArray = [1, 1, 2, 3, 4, 5];
console.log(`${nonUniqueArray} is unique : ${checkIfArrayIsUnique(nonUniqueArray)}`);


这对于一个数组的数组不起作用:[['x','y'], ['a','b'], ['42', 'awerq'], ['12'], ['x', 'y']] - J.E.C.
@J.E.C. 当然可以。第一个和第二个['x','y']元素是内存中不同的对象。 - General Grievance
@J.E.C. ...但是如果你想要处理数组相等的东西,我为此做了一个答案 - General Grievance

25
let arr = [11,22,11,22];

let hasDuplicate = arr.some((val, i) => arr.indexOf(val) !== i);
// hasDuplicate = true

True -> 数组中有重复项

False -> 唯一数组


2
你可以写成一行代码(使用严格相等):a.some((val, i) => a.indexOf(val) !== i); - William Myers
如果您想要一个没有重复项的数组:const filtered = a.filter((val, i) => a.indexOf(val) === i); 注意:这些仅适用于原始值数组。 - William Myers
如果你使用findIndex()而不是indexOf(),它会在找到匹配项后停止,而不是继续处理数组的其余部分。 - bobpal
1
@bobpal,findIndex()和indexOf()都会在找到第一个值后停止搜索,并不会继续搜索整个数组。 - ofir_aghai

23

这应该只需要一个循环就能实现:

function checkIfArrayIsUnique(arr) {
    var map = {}, i, size;

    for (i = 0, size = arr.length; i < size; i++){
        if (map[arr[i]]){
            return false;
        }

        map[arr[i]] = true;
    }

    return true;
}

21

你把返回值的位置颠倒了:

  • 只要找到两个相等的值,就可以得出数组是唯一的并返回false

  • 在最后,检查完所有的配对后,你可以返回true

如果你经常这样做,而且数组很大,你可能需要研究一下排序数组,然后只比较相邻元素的可能性。这将比你目前的方法拥有更好的渐近复杂度。


谢谢你的回答。我认为我的函数是正确的,但返回值被发送到了错误的位置。 - milan m
另外,我在数组中传递了空值,因此得到了错误的答案。无论如何,我的问题已经解决了。 - milan m
@NPE 如果采用排序方式,它如何具有“更好的渐进复杂度”? 它将时间复杂度从O(n)增加到O(nlogn)。该方法确实将空间复杂度从O(n)降低到O(1)。你是在谈论空间复杂度吗? - Vishal

7
假设你的目标浏览器不是IE8,那么下面的代码同样适用:
```javascript // your code here ```
function checkIfArrayIsUnique(myArray) 
{
    for (var i = 0; i < myArray.length; i++) 
    {
        if (myArray.indexOf(myArray[i]) !== myArray.lastIndexOf(myArray[i])) { 
            return false; 
        } 
    } 
    return true;   // this means not unique
}

2
function hasNoDuplicates(arr) {
    return arr.every(num => arr.indexOf(num) === arr.lastIndexOf(num));
}

hasNoDuplicates接受一个数组作为参数,如果没有重复的值,则返回true。如果有任何重复的值,则函数返回false


2
另一个解决方案:
 Array.prototype.checkIfArrayIsUnique = function() {
    this.sort();    
    for ( var i = 1; i < this.length; i++ ){
        if(this[i-1] == this[i])
            return false;
    }
    return true;
    }

2
我不确定这是否是最佳解决方案,直接添加到JavaScript本地原型是不被赞同的,应该将其作为常规函数以防止可能的冲突。 - Michael Ryan Soileau
请注意,如果存在超过2个重复值,则此方法无法正常工作。 - aarjithn
1
为什么这是最佳解决方案?你似乎没有充分的理由来添加原型链,而且它的性能也不如那些在一个循环内完成的答案(因为你首先对数组进行了排序)。也许你可以解释一下为什么认为这是最佳解决方案? - GrayedFox
排序的成本是0(n*log(n)), 而你可以在O(n)内判断是否存在重复项 - 远非最佳解决方案。 - Gil Epshtain
另外,sort 不会改变数组吗?这可能是一个大问题。 - General Grievance

2
这里提供一个O(n)的解决方案:
function hasDupes(arr) {
  /* temporary object */
  var uniqOb = {};
  /* create object attribute with name=value in array, this will not keep dupes*/
  for (var i in arr)
    uniqOb[arr[i]] = "";
  /* if object's attributes match array, then no dupes! */
  if (arr.length == Object.keys(uniqOb).length)
    alert('NO dupes');
  else
    alert('HAS dupes');


}
var arr = ["1/1/2016", "1/1/2016", "2/1/2016"];
hasDupes(arr);

https://jsfiddle.net/7kkgy1j3/


1
没有使用for循环,只能使用Map()
您还可以返回重复项。
(function(a){
  let map = new Map();

  a.forEach(e => {
    if(map.has(e)) {
      let count = map.get(e);
      console.log(count)
      map.set(e, count + 1);
    } else {
      map.set(e, 1);
    }
  });

  let hasDup = false;
  let dups = [];
  map.forEach((value, key) => {
    if(value > 1) {
      hasDup = true;
      dups.push(key);
    }
  });
   console.log(dups);
   return hasDup;
 })([2,4,6,2,1,4]);

1

如果您的数组嵌套其他数组/对象,使用Set方法可能不是您想要的,因为比较两个对象会比较它们的引用。如果您想检查它们包含的值是否相等,需要使用其他方法。以下是几种不同的方法。

方法1:使用JSON.stringify作为键的映射

如果您希望将具有相同包含值的对象视为相等,则可以使用一个简单的方法来使用Map对象进行操作。它使用JSON.stringify为数组中的每个元素创建唯一标识符。

我认为这个运行时在数组上的复杂度将是O(n * m),假设JSON.stringify以线性时间序列化。n是外部数组的长度,m是数组的大小。然而,如果对象变得非常大,这可能会变慢,因为键会变得非常长。这不是一个非常空间有效的实现,但它很简单,并且适用于许多数据类型。

function checkArrayDupeFree(myArray, idFunc) {
    const dupeMap = new Map();
    for (const el of myArray) {
        const id = idFunc(el);
        if (dupeMap.has(id))
            return false;
        dupeMap.set(id, el);
    }
    return true;
}

const notUnique = [ [1, 2], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(notUnique)} has no duplicates? ${checkArrayDupeFree(notUnique, JSON.stringify)}`);

const unique = [ [2, 1], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(unique)} has no duplicates? ${checkArrayDupeFree(unique, JSON.stringify)}`);

当然,你也可以编写自己的id生成函数,但我不确定你能做得比JSON.stringify更好。

方法二:自定义HashMap、Hashcode和Equality实现

如果你有很多大数组,从性能上考虑,最好实现自己的哈希/相等函数,并使用Map作为HashMap。

在以下实现中,我们对数组进行哈希。如果发生冲突,则将一个键映射到一组冲突值,并根据相等函数检查是否有任何数组值匹配。

这种方法的缺点是,你可能需要考虑广泛的类型来制作哈希码/相等函数,具体取决于数组中的内容。

function checkArrayDupeFreeWHashes(myArray, hashFunc, eqFunc) {
    const hashMap = new Map();
    
    for (const el of myArray) {
        const hash = hashFunc(el);
        const hit = hashMap.get(hash);
    
        if (hit == null)
            hashMap.set(hash, [el]);
        else if (hit.some(v => eqFunc(v, el)))
            return false;
        else
            hit.push(el);
    }

    return true;
}

这是一个自定义HashMap的演示。我为数组实现了哈希函数和相等性函数。

function checkArrayDupeFreeWHashes(myArray, hashFunc, eqFunc) {
    const hashMap = new Map();
    
    for (const el of myArray) {
        const hash = hashFunc(el);
        const hit = hashMap.get(hash);
    
        if (hit == null)
            hashMap.set(hash, [el]);
        else if (hit.some(v => eqFunc(v, el)))
            return false;
        else
            hit.push(el);
    }

    return true;
}

function arrayHasher(arr) {
    let hash = 19;
    for (let i = 0; i < arr.length; i++) {
        const el = arr[i];
        const toHash = Array.isArray(el)
            ? arrayHasher(el)
            : el * 23;
        hash = hash * 31 + toHash;
    }

    return hash;
}

function arrayEq(a, b) {
    if (a.length != b.length)
        return false;

    for (let i = 0; i < a.length; i++) {
        if ((Array.isArray(a) || Array.isArray(b)) && !arrayEq(a[i], b[i]))
            return false;
        else if (a[i] !== b[i])
            return false;
    }

    return true;
}

const notUnique = [ [1, 2], [1, 3], [1, 2] ];
const unique = [ [2, 1], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(notUnique)} has no duplicates? ${checkArrayDupeFreeWHashes(notUnique, arrayHasher, arrayEq)}`);
console.log(`${JSON.stringify(unique)} has no duplicates? ${checkArrayDupeFreeWHashes(unique, arrayHasher, arrayEq)}`);


糟糕,不知何故,尽管网站关闭了22分钟,但它仍然让我发布了这个帖子。奇怪。 - General Grievance

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