我有一个非常简单的JavaScript数组,可能包含也可能不包含重复项。

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

我需要删除重复项并将唯一值放入新数组。

我可以指出我尝试过的所有代码,但我认为它们没有用,因为它们不起作用。我也接受jQuery解决方案。

类似的问题:

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


当前回答

虽然ES6解决方案是最好的,但我很困惑,没有人展示了以下解决方案:

function removeDuplicates(arr){
    o={}
    arr.forEach(function(e){
        o[e]=true
    })
    return Object.keys(o)
}

这里需要记住的是,对象必须具有唯一的键。我们正在利用这一点来删除所有重复项。我本以为这是最快的解决方案(在ES6之前)。

请记住,这也会对数组进行排序。

其他回答

厌倦了使用for循环或jQuery的所有糟糕示例。Javascript现在有了完美的工具:排序、映射和减少。

统一减少,同时保持现有订单

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

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

排序更快的uniq

可能有更快的方法,但这一方法相当不错。

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

2015年更新:ES6版本:

在ES6中,您有集合和排列,这使删除所有重复项变得非常容易和高效:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

根据发生情况排序:

有人询问如何根据有多少个唯一名称来排序结果:

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 sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)

https://jsfiddle.net/2w0k5tz8/

function remove_duplicates(array_){
    var ret_array = new Array();
    for (var a = array_.length - 1; a >= 0; a--) {
        for (var b = array_.length - 1; b >= 0; b--) {
            if(array_[a] == array_[b] && a != b){
                delete array_[b];
            }
        };
        if(array_[a] != undefined)
            ret_array.push(array_[a]);
    };
    return ret_array;
}

console.log(remove_duplicates(Array(1,1,1,2,2,2,3,3,3)));

循环、删除重复项并创建克隆数组占位符,因为不会更新数组索引。

向后循环以获得更好的性能(循环不需要一直检查阵列的长度)

VanillaJS:使用像Set这样的Object删除重复项

您可以始终尝试将其放入对象中,然后遍历其关键点:

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Vanilla JS:通过跟踪已经看到的值来删除重复项(订单安全)

或者,对于订单安全版本,使用一个对象来存储所有以前看到的值,并在添加到数组之前检查值。

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6:使用新的Set数据结构(顺序安全)

ECMAScript 6添加了新的Set Data Structure,它允许您存储任何类型的值。Set.values按插入顺序返回元素。

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

示例用法:

a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

像这样使用Array.filter()

var actualArr=[苹果',苹果',香蕉',芒果',草莓',香蕉];console.log('实际数组:'+actualArr);var filteredArr=actualArr.filter(函数(项,索引){if(actualArr.indexOf(item)==索引)退货项目;});console.log('FilteredArray:'+filtedArr);

这可以在ES6中缩短到

actualArr.filter((item,index,self) => self.indexOf(item)==index);

下面是Array.filter()的详细解释

我已经在其他问题上对消除重复进行了详细的比较,但注意到这是真实的地方,我也想在这里分享一下。

我相信这是最好的方法

var-myArray=[100,200,100,100,200,200,200],reduced=Object.keys(myArray.reduce((p,c)=>(p[c]=true,p),{}));console.log(减少);

好啊尽管这一个是O(n),其他是O(n^2),但我很好奇地看到这个reduce/look up表和filter/indexOf组合之间的基准比较(我选择了非常好的Jetendras实现https://stackoverflow.com/a/37441144/4543207). 我准备了一个100K的项目数组,其中填充了0-9999范围内的随机正整数,并删除了重复项。我重复了10次测试,结果的平均值表明他们在性能上不匹配。

在firefox v47中,reduce和lut:14.85ms vs filter和indexOf:2836ms在铬v51中,reduce和lut:23.90ms vs filter和indexOf:1066ms

好吧,到目前为止还不错。但是,让我们这次在ES6风格中正确地完成它。看起来很酷。。!但到目前为止,它将如何对抗强大的lut解决方案对我来说是一个谜。让我们先看看代码,然后对其进行基准测试。

var-myArray=[100,200,100,100,200,200,200],reduce=[…myArray.reduce((p,c)=>p.set(c,true),new Map()).keys()];console.log(减少);

哇,太短了。。!但表演怎么样。。?它很美。。。由于过滤器/indexOf的重量超过了我们的肩,现在我可以测试范围为0..9999的1M个正整数随机项,以获得10次连续测试的平均值。我可以说这一次是真正的比赛。亲自查看结果:)

var ranar=[],red1=a=>Object.keys(a.reduce((p,c)=>(p[c]=true,p),{})),red2=a=>reduced=[…a.reduce((p,c)=>p.set(c,true),new Map()).keys()],avg1=[],avg2=[],ts=0,te=0,res1=[],res2=[],计数=10;for(变量i=0;i<count;i++){ranar=(new Array(1000000).fill(true)).map(e=>Math.floor(Math.random()*100000));ts=performance.now();res1=红色1(ranar);te=performance.now();平均推力(te ts);ts=performance.now();res2=red2(ranar);te=performance.now();平均推力(te ts);}avg1=avg1。减少((p,c)=>p+c)/计数;avg2=avg2.减少((p,c)=>p+c)/计数;console.log(“reduce&luttake:”+avg1+“msec”);console.log(“map&spread take:”+avg2+“msec”);

你会用哪一个。。?别那么快。。。!不要上当受骗。贴图处于置换状态。现在看。。。在上述所有情况下,我们用范围<n的数字填充一个大小为n的数组。如果我们用随机数0..9999填充大小为100的数组。现在让我们在家看地图游戏。这一次是一个100K项的数组,但随机数范围为0..10M。我们将进行100次连续测试,以平均结果。好的,让我们看看赌注……!<-没有错别字

var ranar=[],red1=a=>Object.keys(a.reduce((p,c)=>(p[c]=true,p),{})),red2=a=>reduced=[…a.reduce((p,c)=>p.set(c,true),new Map()).keys()],avg1=[],avg2=[],ts=0,te=0,res1=[],res2=[],计数=100;for(变量i=0;i<count;i++){ranar=(new Array(100000).fill(true)).map(e=>Math.floor(Math.random()*1000000));ts=performance.now();res1=红色1(ranar);te=performance.now();平均推力(te ts);ts=performance.now();res2=red2(ranar);te=performance.now();平均推力(te ts);}avg1=avg1。减少((p,c)=>p+c)/计数;avg2=avg2.减少((p,c)=>p+c)/计数;console.log(“reduce&luttake:”+avg1+“msec”);console.log(“map&spread take:”+avg2+“msec”);

现在,这是Map()的惊人回归。。!也许现在,当你想删除重复时,你可以做出更好的决定。

好吧,我们现在都很开心。但主角总是在一些掌声中走到最后。我相信你们中的一些人想知道Set对象会做什么。既然我们对ES6开放,而且我们知道Map是前几场比赛的赢家,那么让我们将Map与Set进行比较作为决赛。这是一场典型的皇家马德里vs巴塞罗那的比赛。。。还是这样?让我们看看谁会赢得el classico:)

var ranar=[],red1=a=>reduce=[…a.reduce((p,c)=>p.set(c,true),new Map()).keys()],red2=a=>Array.from(新集合(a)),avg1=[],avg2=[],ts=0,te=0,res1=[],res2=[],计数=100;for(变量i=0;i<count;i++){ranar=(new Array(100000).fill(true)).map(e=>Math.floor(Math.random()*100000));ts=performance.now();res1=红色1(ranar);te=performance.now();平均推力(te ts);ts=performance.now();res2=red2(ranar);te=performance.now();平均推力(te ts);}avg1=avg1。减少((p,c)=>p+c)/计数;avg2=avg2.减少((p,c)=>p+c)/计数;console.log(“map&spread take:”+avg1+“msec”);console.log(“set&A.from take:”+avg2+“msec”);

哇!成年男子出乎意料的是,它根本就不是一个古典音乐。更像巴塞罗那FC对阵CA Osasuna:))