常用数组去重的方法,区分包含NaN或{}的情况

298 阅读1分钟

一、利用ES6 Set去重(ES6中最常用)


function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
 //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

不考虑兼容性,这种去重的方法代码最少。但这种方法还无法去掉“{}”空对象。

NaN与其他任何值比较都将不相等 ( 包括与其他 NAN值进行比较)。对象因为存放地址不同,但是从”值“上来说相同的话是否定义为”重复“?

NaN == NaN; //false
NaN === NaN; //false
{} == {}; //false
{} === {}; //false

下文的方法将按照算作“重复”这一点来分析。

二、Map去重

function unique(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(!map .has(arr[i])) {  // 如果有该key值
      map .set(arr[i], true); 
      array .push(arr[i]);
    } 
  } 
  console.log(map)
  return array ;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false,
 undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
 console.log(unique(arr))

不足点:{}没有去重

image.png

优化代码,实现全部去重:

function unique(arr){
  let map=new Map()
  let array=new Array()
  for(let i=0;i<arr.length;i++){//-------------新增部分----------
    if(typeof arr[i]==='object'){//判断是否是对象
      let a=JSON.stringify(arr[i])//JSON序列化变成字符串
      if(!map.has(a)){//map判别是否存在{}字符而不是{}对象
         map.set(a,true)
         array.push(arr[i])
      }
    }else{//---------------------
      if(!map.has(arr[i])){
        map.set(arr[i],true)
        array.push(arr[i])
      }
    }
  }
  return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{} ];

console.log(unique(arr))

输出结果:

image.png

三、一些常用但是存在部分类型未能去重的方法:

1、利用for嵌套for,然后splice去重(ES5中最常用)

NaN和{}没有去重

function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){        
                //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j,1);
                    j--;
                }
            }
        }
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, 
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];

console.log(unique(arr))
// [1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]    
// NaN和{}没有去重,两个null直接消失了

2、利用indexOf去重

NaN和{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];

console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]  
// NaN、{}没有去重

3、利用sort()

NaN和{}没有去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, 
 undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
 
 console.log(unique(arr))
 // [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]   
 //NaN、{}没有去重

4、利用对象的属性不能相同的特点进行去重

两个true直接去掉了,NaN和{}去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var arrry= [];
     var  obj = {};
    for (var i = 0; i < arr.length; i++) {
        if (!obj[arr[i]]) {
            arrry.push(arr[i])
            obj[arr[i]] = 1
        } else {
            obj[arr[i]]++
        }
    }
    return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false,
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];

console.log(unique(arr))
//[1, "true", 15, false, undefined, null, NaN, 0, "a", {…}]    
//两个true直接去掉了,NaN和{}去重

5、利用hasOwnProperty

function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}
var arr = [1,1,'true','true',true,true,15,15,false,false, 
undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];

console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]  
// 利用hasOwnProperty 判断是否存在对象属性

虽然所有的都去重了,但是对象都会被去重

这个是 typeof 那行导致的。 所有对象的原始类型的值都是[object Object],所以所有对象转为字符串都是"[object Object]"。

那么就导致:

var obj1 = {};
typeof (obj1) + obj1 ==>"object" + obj1 ==> "object[object Object]"
var obj2 = {a:1}
typeof (obj2) + obj2 ==>"object" + obj2 ==> "object[object Object]"

其他引用类型则没有这个问题:

var fn = function(){return true};
typeof(fn) + fn ==> "functionfunction() {return true}"
var arr = [1];
typeof(arr) + arr ==> "object1"

所以,该方法遇到对象就GG。

但是,建议不要用typeof进行判断,这个对引用类型判断都会返回 object (除了function/regexp返回 "function")。 而用 Object.prototype.toString.call(value) 来进行判断会更准确些: Object.prototype.toString.call([]) ==> "[object Array]"

当然,这也改变不了在这个方法中,两个以上不同的对象冲突的问题:

var obj1 = {};
Object.prototype.toString.call(obj1) + obj1 ==> "[object Object][object Object]"
var obj2 = {a:1};
Object.prototype.toString.call(obj2) + obj2 ==> "[object Object][object Object]"