获取数组中的所有非唯一值(即:重复/多次出现)

问题描述 投票:366回答:72

我需要检查一个JavaScript数组,看看是否有任何重复的值。最简单的方法是什么?我只需要找到重复的值是什么 - 我实际上并不需要它们的索引或重复它们的次数。

我知道我可以遍历数组并检查匹配的所有其他值,但似乎应该有一个更简单的方法。有任何想法吗?谢谢!

Similar question:

javascript arrays
72个回答
268
投票

您可以对数组进行排序,然后运行它,然后查看下一个(或上一个)索引是否与当前索引相同。假设您的排序算法很好,这应该小于O(n2):

var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var sorted_arr = arr.slice().sort(); // You can define the comparing function here. 
                                     // JS by default uses a crappy string compare.
                                     // (we use slice to clone the array so the
                                     // original array won't be modified)
var results = [];
for (var i = 0; i < sorted_arr.length - 1; i++) {
    if (sorted_arr[i + 1] == sorted_arr[i]) {
        results.push(sorted_arr[i]);
    }
}

console.log(results);

6
投票
var a = [324,3,32,5,52,2100,1,20,2,3,3,2,2,2,1,1,1].sort();
a.filter(function(v,i,o){return i&&v!==o[i-1]?v:0;});

或者当添加到Array的prototype.chain时

//copy and paste: without error handling
Array.prototype.unique = 
   function(){return this.sort().filter(function(v,i,o){return i&&v!==o[i-1]?v:0;});}

见这里:https://gist.github.com/1305056


6
投票

当您需要检查this question中没有重复项时,您可以使用every()方法:

[1, 2, 3].every(function(elem, i, array){return array.lastIndexOf(elem) === i}) // true

[1, 2, 1].every(function(elem, i, array){return array.lastIndexOf(elem) === i}) // false

请注意,every()不适用于IE 8及更低版本。

我使用lastIndexOf()因为它可能比indexOf()更有效,如果every()做的函数回调是按索引顺序进行的,但是这没有得到证实。

在CoffeeScript中我使用这个:

Array::duplicates = -> not @every((elem, i, array) -> array.lastIndexOf(elem) is i)

[1, 2, 3].duplicates() // false
[1, 2, 1].duplicates() // true

6
投票

Find unique values from 3 arrays (or more):

Array.prototype.unique = function () {
    var arr = this.sort(), i; // input must be sorted for this to work
    for( i=arr.length; i--; )
      arr[i] === arr[i-1] && arr.splice(i,1); // remove duplicate item

    return arr;
}

var arr =  [1,2,2,3,3,4,5,6,2,3,7,8,5,9],
    arr2 = [1,2,511,12,50],
    arr3 = [22],
    unique = arr.concat(arr2, arr3).unique();

console.log(unique);  // [22, 50, 12, 511, 2, 1, 9, 5, 8, 7, 3, 6, 4]

Just a polyfill for array indexOf for old browsers:

if (!Array.prototype.indexOf){
   Array.prototype.indexOf = function(elt /*, from*/){
     var len = this.length >>> 0;

     var from = Number(arguments[1]) || 0;
     from = (from < 0) ? Math.ceil(from) : Math.floor(from);
     if (from < 0)
        from += len;

     for (; from < len; from++){
        if (from in this && this[from] === elt)
           return from;
     }
     return -1;
  };
}

使用“inArray”的jQuery解决方案:

if( $.inArray(this[i], arr) == -1 )

ES2015

var arr =  [1,2,2,3,3,4,5,6,2,3,7,8,5,22],
    arr2 = [1,2,511,12,50],
    arr3 = [22],
    unique;

// Combine all the arrays to a single one
unique = arr.concat(arr2, arr3);
// create a new (dirty) Array with only the unique items
unique = unique.map((item,i) => unique.includes(item, i+1) ? item : '' )
// Cleanup - remove duplicate & empty items items 
unique = [...new Set(unique)].filter(n => n);

console.log(unique);

而不是添加'Array.prototype.indexOf'


6
投票

这是我的简单和一线解决方案。

它首先搜索不是唯一的元素,然后使用Set使找到的数组唯一。

所以我们最后有一系列重复。

var array = [1, 2, 2, 3, 3, 4, 5, 6, 2, 3, 7, 8, 5, 22, 1, 2, 511, 12, 50, 22];

console.log([...new Set(
  array.filter((value, index, self) => self.indexOf(value) !== index))]
);

5
投票

使用es6对象解构和减少快速而优雅的方式

它在O(n)中运行(在数组上迭代1次)并且不重复出现超过2次的值

const arr = ['hi', 'hi', 'hi', 'bye', 'bye', 'asd']
const {
  dup
} = arr.reduce(
  (acc, curr) => {
    acc.items[curr] = acc.items[curr] ? acc.items[curr] += 1 : 1
    if (acc.items[curr] === 2) acc.dup.push(curr)
    return acc
  }, {
    items: {},
    dup: []
  },
)

console.log(dup)
// ['hi', 'bye']

5
投票

这是我的建议(ES6):

let a = [1, 2, 3, 4, 2, 2, 4, 1, 5, 6]
let b = [...new Set(a.sort().filter((o, i) => o !== undefined && a[i + 1] !== undefined && o === a[i + 1]))]

// b is now [1, 2, 4]

4
投票

使用ES6(或使用Babel或Typescript),您可以简单地执行以下操作:

var duplicates = myArray.filter(i => myArray.filter(ii => ii === i).length > 1);

https://es6console.com/j58euhbt/


4
投票

使用ES6语法的简单代码(返回重复排序的数组):

let duplicates = a => {d=[]; a.sort((a,b) => a-b).reduce((a,b)=>{a==b&&!d.includes(a)&&d.push(a); return b}); return d};

如何使用:

duplicates([1,2,3,10,10,2,3,3,10]);

3
投票

下面的函数(已经提到的eliminateDuplicates函数的变体)似乎可以解决问题,返回test2,1,7,5作为输入[“test”,“test2”,“test2”,1,1,1,2 ,3,4,5,6,7,7,10,22,43,1,5,8]

请注意,JavaScript中的问题比大多数其他语言更奇怪,因为JavaScript数组几乎可以容纳任何内容。请注意,使用排序的解决方案可能需要提供适当的排序功能 - 我还没有尝试过该路由。

此特定实现适用于(至少)字符串和数字。

function findDuplicates(arr) {
    var i,
        len=arr.length,
        out=[],
        obj={};

    for (i=0;i<len;i++) {
        if (obj[arr[i]] != null) {
            if (!obj[arr[i]]) {
                out.push(arr[i]);
                obj[arr[i]] = 1;
            }
        } else {
            obj[arr[i]] = 0;            
        }
    }
    return out;
}

3
投票

这是一个非常轻松简单的方法:

var codes = dc_1.split(',');
var i = codes.length;
while (i--) {
  if (codes.indexOf(codes[i]) != i) {
    codes.splice(i,1);
  }
}

204
投票

如果您想消除重复项,请尝试以下解决方案:

function eliminateDuplicates(arr) {
  var i,
      len = arr.length,
      out = [],
      obj = {};

  for (i = 0; i < len; i++) {
    obj[arr[i]] = 0;
  }
  for (i in obj) {
    out.push(i);
  }
  return out;
}

资料来源:http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/


3
投票

var arr = [2, 1, 2, 2, 4, 4, 2, 5];

function returnDuplicates(arr) {
  return arr.reduce(function(dupes, val, i) {
    if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
      dupes.push(val);
    }
    return dupes;
  }, []);
}

alert(returnDuplicates(arr));

此函数避免了排序步骤,并使用reduce()方法将重复项推送到新数组(如果它尚不存在)。


3
投票

使用“includes”来测试元素是否已存在。

var arr = [1, 1, 4, 5, 5], darr = [], duplicates = [];

for(var i = 0; i < arr.length; i++){
  if(darr.includes(arr[i]) && !duplicates.includes(arr[i]))
    duplicates.push(arr[i])
  else
    darr.push(arr[i]);
}

console.log(duplicates);
<h3>Array with duplicates</h3>
<p>[1, 1, 4, 5, 5]</p>
<h3>Array with distinct elements</h3>
<p>[1, 4, 5]</p>
<h3>duplicate values are</h3>
<p>[1, 5]</p>

3
投票

ES6提供了Set数据结构,它基本上是一个不接受重复的数组。使用Set数据结构,可以非常轻松地在数组中查找重复项(仅使用一个循环)。

这是我的代码

function findDuplicate(arr) {
var set = new Set();
var duplicates = new Set();
  for (let i = 0; i< arr.length; i++) {
     var size = set.size;
     set.add(arr[i]);
     if (set.size === size) {
         duplicates.add(arr[i]);
     }
  }
 return duplicates;
}

3
投票

我刚刚想出了一种使用Array过滤器实现此目的的简单方法

    var list = [9, 9, 111, 2, 3, 4, 4, 5, 7];
    
    // Filter 1: to find all duplicates elements
    var duplicates = list.filter(function(value,index,self) {
       return self.indexOf(value) !== self.lastIndexOf(value) && self.indexOf(value) === index;
    });
    
    console.log(duplicates);

3
投票

遵循逻辑将更容易,更快捷

// @Param:data:Array that is the source 
// @Return : Array that have the duplicate entries
findDuplicates(data: Array<any>): Array<any> {
        return Array.from(new Set(data)).filter((value) => data.indexOf(value) !== data.lastIndexOf(value));
      }

好处 :

  1. 单行:-P
  2. 所有内置数据结构有助于提高效率
  3. 快点

逻辑描述:

  1. 转换为设置以删除所有重复项
  2. 迭代设定值
  3. 使用每个设置值检查源数组中的条件“值第一个索引不等于最后一个索引”==>然后推断为重复,否则它是'唯一'

注意:map()和filter()方法效率高,速度快。


2
投票

只是为了上面添加一些理论。

查找重复项在比较模型中有一个O(n * log(n)的下限。从理论上讲,你不能比第一次排序更好,然后通过列表顺序删除你找到的任何重复项。

如果要在线性(O(n))预期时间内找到重复项,可以对列表中的每个元素进行哈希处理;如果发生碰撞,请将其删除/标记为重复,然后继续。


2
投票

仅限ES5(即,它需要IE8及更低版本的filter()polyfill):

var arrayToFilter = [ 4, 5, 5, 5, 2, 1, 3, 1, 1, 2, 1, 3 ];

arrayToFilter.
    sort().
    filter( function(me,i,arr){
       return (i===0) || ( me !== arr[i-1] );
    });

2
投票
var input = ['a', 'b', 'a', 'c', 'c'],
    duplicates = [],
    i, j;
for (i = 0, j = input.length; i < j; i++) {
  if (duplicates.indexOf(input[i]) === -1 && input.indexOf(input[i], i+1) !== -1) {
    duplicates.push(input[i]);
  }
}

console.log(duplicates);

2
投票

我认为以下是最简单,最快速的O(n)方式,可以完全满足您的要求:

function getDuplicates( arr ) {
  var i, value;
  var all = {};
  var duplicates = [];

  for( i=0; i<arr.length; i++ ) {
    value = arr[i];
    if( all[value] ) {
      duplicates.push( value );
      all[value] = false;
    } else if( typeof all[value] == "undefined" ) {
      all[value] = true;
    }
  }

  return duplicates;
}

或者对于ES5或更高版本:

function getDuplicates( arr ) {
  var all = {};
  return arr.reduce(function( duplicates, value ) {
    if( all[value] ) {
      duplicates.push(value);
      all[value] = false;
    } else if( typeof all[value] == "undefined" ) {
      all[value] = true;
    }
    return duplicates;
  }, []);
}

2
投票

修改@ RaphaelMontanaro的解决方案,借用@Nosredna的博客,如果您只想从阵列中识别重复元素,可以执行以下操作。

function identifyDuplicatesFromArray(arr) {
        var i;
        var len = arr.length;
        var obj = {};
        var duplicates = [];

        for (i = 0; i < len; i++) {

            if (!obj[arr[i]]) {

                obj[arr[i]] = {};

            }

            else
            {
                duplicates.push(arr[i]);
            }

        }
        return duplicates;
    }

感谢优雅的解决方案@Nosredna!


154
投票

这是我从重复的线程(!)得到的答案:

在撰写此条目2014时 - 所有示例都是for循环或jQuery。 Javascript有完美的工具:排序,映射和减少。

找到重复的项目

var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

var uniq = names
  .map((name) => {
    return {
      count: 1,
      name: name
    }
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var duplicates = Object.keys(uniq).filter((a) => uniq[a] > 1)

console.log(duplicates) // [ 'Nancy' ]

更多功能语法:

@ Dmytro-Laptin指出要删除一些代码。这是相同代码的更紧凑版本。使用一些ES6技巧和更高阶函数:

const names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

const count = names =>
  names.reduce((a, b) => ({ ...a,
    [b]: (a[b] || 0) + 1
  }), {}) // don't forget to initialize the accumulator

const duplicates = dict =>
  Object.keys(dict).filter((a) => dict[a] > 1)

console.log(count(names)) // { Mike: 1, Matt: 1, Nancy: 2, Adam: 1, Jenny: 1, Carl: 1 }
console.log(duplicates(count(names))) // [ 'Nancy' ]

2
投票

我不喜欢大多数答案。

为什么?太复杂,代码太多,代码效率低下,很多都没有回答问题,即找到重复项(而不是给出没有重复项的数组)。

下一个函数返回所有重复项:

function GetDuplicates(arr) {
  var i, out=[], obj={};
  for (i=0; i < arr.length; i++) 
    obj[arr[i]] == undefined ? obj[arr[i]] ++ : out.push(arr[i]);
  return out;
}  

因为大多数情况下返回所有重复项是没有用的,只是为了告诉存在哪些重复值。在这种情况下,您返回一个具有唯一重复项的数组;-)

function GetDuplicates(arr) {
  var i, out=[], obj={};
  for (i=0; i < arr.length; i++)
    obj[arr[i]] == undefined ? obj[arr[i]] ++ : out.push(arr[i]);
  return GetUnique(out);
}

function GetUnique(arr) {
  return $.grep(arr, function(elem, index) {
    return index == $.inArray(elem, arr);
  });
}

也许别人也这么认为。


44
投票

在数组中查找重复值

这应该是在数组中实际查找重复值的最短方法之一。正如OP特别要求的那样,这不会删除重复但会找到它们。

var input = [1, 2, 3, 1, 3, 1];

var duplicates = input.reduce(function(acc, el, i, arr) {
  if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc;
}, []);

document.write(duplicates); // = 1,3 (actual array == [1, 3])

这不需要排序或任何第三方框架。它也不需要手动循环。它适用于每个值indexOf()(或更清楚:strict comparision operator)支持。

因为reduce()indexOf()它至少需要IE 9。


29
投票

您可以添加此功能,或调整它并将其添加到Javascript的Array原型中:

Array.prototype.unique = function () {
    var r = new Array();
    o:for(var i = 0, n = this.length; i < n; i++)
    {
        for(var x = 0, y = r.length; x < y; x++)
        {
            if(r[x]==this[i])
            {
                alert('this is a DUPE!');
                continue o;
            }
        }
        r[r.length] = this[i];
    }
    return r;
}

var arr = [1,2,2,3,3,4,5,6,2,3,7,8,5,9];
var unique = arr.unique();
alert(unique);

27
投票

更新:以下使用优化的组合策略。它优化了原始查找以受益于散列O(1)查找时间(在基元数组上运行unique是O(n))。通过在迭代时标记具有唯一id的对象来优化对象查找,因此识别重复对象对于每个项目也是O(1)并且对于整个列表也是O(n)。唯一的例外是冻结的项目,但这些项目很少见,并且使用数组和indexOf提供回退。

var unique = function(){
  var hasOwn = {}.hasOwnProperty,
      toString = {}.toString,
      uids = {};

  function uid(){
    var key = Math.random().toString(36).slice(2);
    return key in uids ? uid() : uids[key] = key;
  }

  function unique(array){
    var strings = {}, numbers = {}, others = {},
        tagged = [], failed = [],
        count = 0, i = array.length,
        item, type;

    var id = uid();

    while (i--) {
      item = array[i];
      type = typeof item;
      if (item == null || type !== 'object' && type !== 'function') {
        // primitive
        switch (type) {
          case 'string': strings[item] = true; break;
          case 'number': numbers[item] = true; break;
          default: others[item] = item; break;
        }
      } else {
        // object
        if (!hasOwn.call(item, id)) {
          try {
            item[id] = true;
            tagged[count++] = item;
          } catch (e){
            if (failed.indexOf(item) === -1)
              failed[failed.length] = item;
          }
        }
      }
    }

    // remove the tags
    while (count--)
      delete tagged[count][id];

    tagged = tagged.concat(failed);
    count = tagged.length;

    // append primitives to results
    for (i in strings)
      if (hasOwn.call(strings, i))
        tagged[count++] = i;

    for (i in numbers)
      if (hasOwn.call(numbers, i))
        tagged[count++] = +i;

    for (i in others)
      if (hasOwn.call(others, i))
        tagged[count++] = others[i];

    return tagged;
  }

  return unique;
}();

如果您有可用的ES6集合,则可以使用更简单且速度更快的版本。 (这里是IE9 +和其他浏览器的垫片:https://github.com/Benvie/ES6-Harmony-Collections-Shim

function unique(array){
  var seen = new Set;
  return array.filter(function(item){
    if (!seen.has(item)) {
      seen.add(item);
      return true;
    }
  });
}

17
投票
var a = ["a","a","b","c","c"];

a.filter(function(value,index,self){ return (self.indexOf(value) !== index )})

17
投票

这应该得到你想要的,只是重复。

function find_duplicates(arr) {
  var len=arr.length,
      out=[],
      counts={};

  for (var i=0;i<len;i++) {
    var item = arr[i];
    counts[item] = counts[item] >= 1 ? counts[item] + 1 : 1;
    if (counts[item] === 2) {
      out.push(item);
    }
  }

  return out;
}

find_duplicates(['one',2,3,4,4,4,5,6,7,7,7,'pig','one']); // -> ['one',4,7] in no particular order.

13
投票

使用underscore.js

function hasDuplicate(arr){
    return (arr.length != _.uniq(arr).length);
}
© www.soinside.com 2019 - 2024. All rights reserved.