为了账号安全,请及时绑定邮箱和手机立即绑定

跪求!是否存在通用的数组去重方法?

跪求!是否存在通用的数组去重方法?

萧十郎 2019-08-21 12:01:36
数组去重,很多时候都会用到,在具体项目中因为场景确定,很容易写出一个简单的去重方法,但大多是否具有针对性,没法适用于各类,一劳永逸。当然此处的场景所说的去重指的是去除内容大致相同的数据,不管是否同一个引用。如下这段代码,如果希望输出的是[[1],{a:2},{a:1},[2]]letobj={a:1};letarr=[2];lettest=[[1],[1],{a:2},{a:2},obj,obj,arr,arr];Array.prototype.unique=function(){...}console.log(test.unique());通常我们利用Set和Array互转实现去重,但是该方式在判断引用类型时,并没那么给力,只有相同的引用才会被判定为相等。console.log([...newSet(test)]);//[[1],[1],{a:2},{a:2},{a:1},[2]];如果采用对象的方式,对付普通的数据还好,碰上这种情况也是无能为力Array.prototype.unique=function(){varres=[];varjson={};for(vari=0;i
查看完整描述

3 回答

?
慕先生4543998

TA贡献1条经验 获得超1个赞

数组去重通用API

要求就是写一个数组去重的API,第一个参数是一个数组,第二个参数是一个函数(对象数组去重的规则)。

思路就是当它是数组的时候,直接用这个Set数据结构去重,如果是个对象数据的话,就新建一个Map,按照传入的函数返回的值,存入Map。这里用到了filter,它是用传入的函数测试所有的元素,并且返回所有通过测试的元素。

function fn(arr,rule){
    if(!rule){
        return Array.from(new Set([...arr]));
    }else{
        const res=new Map();
        return arr.filter((a)=>!res.has(rule(a))&&res.set(rule(a),1));
    }
}


查看完整回答
1 反对 回复 2020-03-22
?
慕村225694

TA贡献1880条经验 获得超4个赞

看了lodash的源码迷迷糊糊的,还是用自己的方式来实现一把。部分人可能对题意所说的通用有着不一样的理解,所以觉得束手束脚有点难。此处的场景是确定,我的思路及实现如下,有人能提出点优化建议更好。
//数组去重
//deep不为空表示对引用类型的内容进行抽象判断
Array.prototype.unique=function(deep){
letarr=[...newSet(this)];
deep&&deepUnique(arr);
returnarr;
}
//深度去重,只对引用类型的数据进行再次内容对比
functiondeepUnique(arr){
letobjArr=[];
arr.forEach((item,i)=>{
if(['object','function'].includes(typeofitem)){
objArr.unshift({idx:i,val:item});
}
})
if(objArr.length<=1)return;
for(leti=arr.length-1;i>=0;i--){
if(['object','function'].includes(typeofarr[i])){
objArr.some((item,idx)=>{
if((item.idxobjArr.splice(idx,1);//缩短遍历长度
arr.splice(i,1);//移除重复数据
returntrue;
}
})
}
}
}
//是否内容相同
functionisSameObj(val0,val1){
letres=false;
lettype=isSameType(val0,val1);
if(!type)returnres;
switch(type){
case'HTMLDivElement':
res=val0.outerHTML===val1.outerHTML;
break;
case'File':
res=JSON.stringify(val0)===JSON.stringify(val1);
break;
case'class':
case'Object':
letkeys0=Object.keys(val0);
letkeys1=Object.keys(val1);
if(JSON.stringify(keys0)===JSON.stringify(keys1)){
letarr0=keys0.map(k=>val0[k]);
letarr1=keys1.map(k=>val1[k]);
res=isSameArray(arr0,arr1);
}
break;
case'Array':
res=isSameArray(val0,val1);
break;
case'Function':
res=val0.toString()===val1.toString();
break;
case'Error':
res=val0.stack===val1.stack;
break;
default:
break;
}
returnres;
}
//是否相同的类型
functionisSameType(val0,val1){
lettype=false;
if(val0.constructor==val1.constructor){
letarr=val0.constructor.toString().substr(0,100).split('');
type=arr[0]==='function'?arr[1].slice(0,-2):arr[1];
}
returntype;
}
//数组的值是否相同
functionisSameArray(arr0,arr1){
if(arr0.length!==arr1.length)returnfalse;
letsame=true;
for(leti=0;iif(typeofarr0[i]!==typeofarr1[i]){//类型不同
same=false;
break;
}
if(arr0[i]&&arr1[i]&&['object','function'].includes(typeofarr0[i])){//非空引用类型
if(!isSameObj(arr0[i],arr1[i])){
same=false;
break;
}
}elseif((arr0[i]!==arr1[i])){
if(!((typeofarr0[i]==='number')&&isNaN(arr0[i])&&isNaN(arr1[i]))){//除了NaN!=NaN的情况
same=false;
break;
}
}
}
returnsame;
}
测试结果如下
letobj={a:1};
letarr=[2];
lettest=[[1],[1],{a:2},{a:2},obj,obj,arr,arr];
console.log(test.unique());//[[1],[1],{a:2},{a:2},{a:1},[2]]
console.log(test.unique(true));//[[1],{a:2},{a:1},[2]]
                            
查看完整回答
反对 回复 2019-08-21
?
慕莱坞森

TA贡献1810条经验 获得超4个赞

首先谈下我的理解,不可能有一劳永逸的方法。同意楼上去除复杂引用类型本身就是一个高度自定化的功能,需要自身去定义,按你的说法,1.待去重数组内的类型是不确定的2.不同的类型,甚至你自己定义的class,都有不同的"相等"的含义,比如定义user的类,只需id一样。在java中,判断对象相等需要重写equals和hashCode方法,equals返回true就是相等了,现在连类都未知,怎么去实现它的equals方法呢?ps:感觉实际开发中这种包罗万象的数组存在的可能性微乎其微,可以参考下lodash的unionWith函数_.unionWith([arrays],[comparator])
varobjects=[{'x':1,'y':2},{'x':2,'y':1}];
varothers=[{'x':1,'y':1},{'x':1,'y':2}];
_.unionWith(objects,others,_.isEqual);
//=>[{'x':1,'y':2},{'x':2,'y':1},{'x':1,'y':1}]
把判断是否相等的代码comparator作为参数,如果有多种类型,就在comparator里面加个类型判断就好了。
                            
查看完整回答
反对 回复 2019-08-21
  • 3 回答
  • 0 关注
  • 399 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信