我有一个非常简单的JavaScript数组,可能包含也可能不包含重复项。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
我需要删除重复项并将唯一值放入新数组。
我可以指出我尝试过的所有代码,但我认为它们没有用,因为它们不起作用。我也接受jQuery解决方案。
类似的问题:
获取数组中的所有非唯一值(即:重复/多次出现)
我有一个非常简单的JavaScript数组,可能包含也可能不包含重复项。
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
我需要删除重复项并将唯一值放入新数组。
我可以指出我尝试过的所有代码,但我认为它们没有用,因为它们不起作用。我也接受jQuery解决方案。
类似的问题:
获取数组中的所有非唯一值(即:重复/多次出现)
当前回答
复杂度为O(n)的Vanilla JS解决方案(对于这个问题最快)。如果需要,修改hashFunction以区分对象(例如1和“1”)。第一种解决方案避免了隐藏循环(在Array提供的函数中常见)。
var dedupe = function(a)
{
var hash={},ret=[];
var hashFunction = function(v) { return ""+v; };
var collect = function(h)
{
if(hash.hasOwnProperty(hashFunction(h)) == false) // O(1)
{
hash[hashFunction(h)]=1;
ret.push(h); // should be O(1) for Arrays
return;
}
};
for(var i=0; i<a.length; i++) // this is a loop: O(n)
collect(a[i]);
//OR: a.forEach(collect); // this is a loop: O(n)
return ret;
}
var dedupe = function(a)
{
var hash={};
var isdupe = function(h)
{
if(hash.hasOwnProperty(h) == false) // O(1)
{
hash[h]=1;
return true;
}
return false;
};
return a.filter(isdupe); // this is a loop: O(n)
}
其他回答
我已经在其他问题上对消除重复进行了详细的比较,但注意到这是真实的地方,我也想在这里分享一下。
我相信这是最好的方法
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:))
对thg435的优秀答案稍作修改,以使用自定义比较器:
function contains(array, obj) {
for (var i = 0; i < array.length; i++) {
if (isEqual(array[i], obj)) return true;
}
return false;
}
//comparator
function isEqual(obj1, obj2) {
if (obj1.name == obj2.name) return true;
return false;
}
function removeDuplicates(ary) {
var arr = [];
return ary.filter(function(x) {
return !contains(arr, x) && arr.push(x);
});
}
使用jQuery快速而肮脏:
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
顶级答案的复杂度为O(n²),但这可以通过使用对象作为哈希来实现,只需O(n):
function getDistinctArray(arr) {
var dups = {};
return arr.filter(function(el) {
var hash = el.valueOf();
var isDup = dups[hash];
dups[hash] = true;
return !isDup;
});
}
这将适用于字符串、数字和日期。如果您的数组包含对象,则上述解决方案将不起作用,因为当强制为字符串时,它们都将具有“[objectObject]”(或类似的值),这不适合作为查找值。通过在对象本身上设置标志,可以获得对象的O(n)实现:
function getDistinctObjArray(arr) {
var distinctArr = arr.filter(function(el) {
var isDup = el.inArray;
el.inArray = true;
return !isDup;
});
distinctArr.forEach(function(el) {
delete el.inArray;
});
return distinctArr;
}
2019年编辑:JavaScript的现代版本使这个问题更容易解决。无论数组中是否包含对象、字符串、数字或任何其他类型,都可以使用Set。
function getDistinctArray(arr) {
return [...new Set(arr)];
}
实现如此简单,不再需要定义函数。
这只是另一种解决方案,但与其他解决方案不同。
function diffArray(arr1, arr2) {
var newArr = arr1.concat(arr2);
newArr.sort();
var finalArr = [];
for(var i = 0;i<newArr.length;i++) {
if(!(newArr[i] === newArr[i+1] || newArr[i] === newArr[i-1])) {
finalArr.push(newArr[i]);
}
}
return finalArr;
}