zoukankan      html  css  js  c++  java
  • JS- 数组去重方法整理

    indexOf

    简单而且可以加中文,但是兼容性不好,indexOF兼容到ie9

     1 function uniq(arr) {
     2     var temp = [];
     3     for (let i = 0; i < arr.length; i++) {
     4         if (temp.indexOf(arr[i]) == -1) {
     5             temp.push(arr[i])
     6         }
     7     }
     8     return temp;
     9 }
    10 console.log(uniq([1, 2, 3, 2, 34, "a", "b", "a", 2, 5, 2, 62, ]));

    【排序后相邻去除法】

    简单,去重排序一起做。但必须是纯数组,不能在数字中混入字符串

     1 function uniq3(array) {
     2   array.sort((a, b) => {
     3     return a - b
     4   });
     5   var temp = [array[0]]; //关键点,先把第一个放进去
     6   for (let i = 1; i < array.length; i++) { //i从1开始
     7     if (array[i] !== temp[temp.length - 1]) {
     8       temp.push(array[i])
     9     }
    10   }
    11   return temp;
    12 }
    13 console.log(uniq3([1, 2, 1, 2, 12, 13, 235, 3])); // 缺点就是不能加入字符串
    14 console.log(uniq3([1, 2, "b", 1, 2, 12, 13, 235, 3, "a"])); // 缺点就是不能加入字符串

     

    【根据上边改成数组中字符串排序去重法】

     1 function uniq4(array) {
     2   array.sort((a, b) => {
     3     return (a+'').charCodeAt() - (b+'').charCodeAt()
     4   });
     5   var temp = [array[0]]; //关键点,先把第一个放进去
     6   for (let i = 1; i < array.length; i++) { //i从1开始
     7     if (array[i] !== temp[temp.length - 1]) {
     8       temp.push(array[i])
     9     }
    10   }
    11   return temp;
    12 }
    13 console.log(uniq4(['w', 'a', "b", 'w', 'n', 's', 's', 'b', 'e', "a"])); // 缺点就是不能混入别的

     

    【存到对象里一一对比,去重无排序】

     

    思路 - 利用对象的hash键值对方法,快速获取的特点

    准备一个对象和一个新数组,

    循环原数组时,将数组的值当做对象的键,且对应值为true/1皆可),

    然后每次拿原数组的下一个值都去判断一下对象中是否有这个值,

    没有再push到新数组中去,最后返回新数组。

     1 uniqArr: function (arr) {
     2     var arr = Array.prototype.slice.call(arr),//这里防止伪数组,容错
     3         obj = {},
     4         newArr = [];
     5         obj[arr[0]] = true;//这里没必要
     6         newArr.push(arr[0]);
     7     arr.forEach((el) => {
     8         if (!obj[el]) {//但是这样写,又一个问题,见下边总结
     9             obj[el] = true;//这里写成true,if里少一步隐式转换
    10             newArr.push(el);
    11         }
    12     });
    13     return newArr;
    14 }

     

    总结:

     

    这种写法有一个问题,就是当我们arr中有多个不一样的对象元素(比如[1,{val:212},'a',{c: 1},false,{d:2}]这样时,会发现,只留下了第一个对象{val:212}这样。

     

    控制台效果:

     

    这是因为一个对象值当做obj的键名时会被toString化,得到的是obj['[object Object]'],后边再有对象时在forEach判断里条件不会成立,就不会被push到新数组,因此也就会被过滤掉了。

     

    其原理同这个题:

    1 var a = {};b = {m: 1};c ={n:2};
    2 
    3 a[b] = 'b';
    4 
    5 a[c] = 'c';

    a当中,键名不叫b也不叫c,而是b这个对象被Object.prototype.toString化后的字符串[object Object]。就是同一个原理。

     

    因为我们使用[]来给对象a追加属性,在[]中,是期望是一个字符串类型的值,所以里边的值会被"字符串化"

     

    也就是当我们传的变量是一个{}时,对象会调用自身原型上的Object.prototype.toString进行转化变成字符串[object Object]

     

    当我们变量是一个数字12,会调用数字的构造类Number原型上重写的toString方法得到一个字符串'12'

     

    反正都是会转成字符串。即使当变量转为字符串后为特殊值(如NaN)等也不例外:

     

     

    以上边的缺点引出下边的方法:

      

    【一行代码实现数组去重】

    1 let newArr = [...new Set([1, 2, 23, 1, "gjf", 1, 6, "gjf","gfj"])]
    2 console.log(newArr)

    // 核心

    […new Set([..需要去重的数组..])]

     

     

    【缺点方法的解决】

     

    上边说道【存到对象里,一一对比进行去重】方法时,有一个缺点是数组内嵌套了对象、数组这些值时,

     

    因为obj[键值]会被字符串化,导致后边出现的多个对象都会被当做重复处理,

     

    所以我们需要在往obj里存值时对这种情况进行一下处理

     

    需要去重的数组:

    先看最初的写法:

     1 function uniq0517(arr){
     2     var obj = {};
     3     var rst = [];
     4     arr.forEach(el => {
     5         if(!obj[el]){
     6             obj[el] = true;
     7             rst.push(el);
     8         }
     9     });
    10     console.log(obj);// 注意下打印的obj
    11     return rst;
    12 }

    目标:去掉重复的对象和数组以及其他原始值,但是保留不同的对象和数组.

     

    去重后的结果:

     

     

    观察发现,除了对象外,都完成了很好的效果。

     

    看打印的obj结果分析问题:

     

    因为数组在obj的中括号[]里进行toString后是字符串'1,23,4'这样,所以可以比较不同。

     

    但是对象不行。对象一律是[object Object]这样,需要对对象进行处理:

     

    我的思路是,如果检测值为对象(就是普通的对象,而不是时间、正则等这种对象,

     

    所以需要toString检测)时,就把这个对象先JSON.stringify化了。再去进行对比。

     

    就像下面这样,JSON.stringify过后的对象绝对可以判断是否全等。

     

     

    因此数组去重改造过后的代码如下:

     1 function uniq0517(arr){
     2     var obj = {},
     3         rst = [],
     4         tempEl = '';
     5     arr.forEach(el => {
     6         if(Object.prototype.toString.call(el) === '[object Object]'){
     7             // 证明这个值是一个对象,就事先将对象字符串化。而不是让他调用自己的toString
     8             tempEl = JSON.stringify(el);
     9         }else{
    10             // 其他类型的值时不做改变
    11             tempEl = el;
    12         }
    13         if(!obj[tempEl]){
    14             obj[tempEl] = true;
    15             rst.push(el);
    16         }
    17     });
    18     console.log(obj);
    19     return rst;
    20 }

     

    完美经过检验!

     

     

    最后拓展,想着把临时obj换成Map数据结构改造一下:

     

     1 function uni10519(arr){
     2     // 用Map实现去重 - 但凡引用值都不能去重
     3     let map = new Map(),
     4         rst = [];
     5     arr.forEach(el => {
     6         if(!map.get(el)){// 已有的一个对象再获取,因为地址不同,所以得到false
     7             map.set(el,true);
     8             rst.push(el);
     9         }
    10     });
    11     console.log(map);
    12     return rst;
    13 }
    14 console.log('map试水失败',uni10519(arr20190517));

    算了吧。。。引用值地址不同,没法取出来作对比。

     

    同样的,对于这种超级复杂类型的数组,进行去重的话,还得用我最原始的方式进行一一比对。否则set也扑街了:

    1 function uni10519Set(arr) {
    2     // 用Set实现去重 
    3     return [...new Set(arr)];
    4 }
    5 console.log('set试水失败',uni10519Set(arr20190517));// 对象、数组什么的引用值都不能比较相等

     

     

     

  • 相关阅读:
    机器学习的分类与主要算法对比
    关于在JSP页面中为什么一定要用${pageContext.request.contextPath}来获取项目路径,而不能用${request.contextPath}?
    Spring Boot静态资源处理
    Tomcat关闭后,重新启动,session中保存的对象为什么还存在解决方法
    Tomcat 7源码学习笔记 -9 tomcat重启后session仍然保留
    mysql-sql语句中变量的使用
    js检测对象中是否存在某个属性
    mysql :=和=的区别
    sql面试题(学生表_课程表_成绩表_教师表)
    ddd
  • 原文地址:https://www.cnblogs.com/padding1015/p/11589068.html
Copyright © 2011-2022 走看看