专栏首页CRPER折腾记JS数组去重!!!一篇不怎么靠谱的"深度"水文

JS数组去重!!!一篇不怎么靠谱的"深度"水文

前言

数组去重,这是一个老梗了...今天我又拿出来说了... 我们在考虑全面一点的情况下,数组去重的实现,比如针对NaN,undefined,{}; 这其中涉及的知识点挺多,不信跟着走一波; 这里不考虑浏览器兼容性这些破问题,因为涉及ES5&6


基础版-只包含一些可以直接比较的数值

测试数组

[1,1,'','','e','e',true,'true',true,false,false,'false',undefined,'undefined',undefined,null,'null',null]

  • [ES5]万能的for方法
function uniqueUseFor(array) {
  var temp = []; //一个临时数组

  //遍历当前数组
  for (var i = 0, j = array.length; i < j; i++) {
    //很直白,新数组内判断是否有这个值,没有的情况下,就推入该新数组
    temp.indexOf(array[i]) === -1 ? temp.push(array[i]) : '';
  }
  return temp;
}

// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]复制代码
  • [ES5]内置的forEach方法
function uniqueUseForEach(array) {
  // 传入值必须存在,且长度小于等于1的时候直接返回数组
  if (array && array.length <= 1) {
    return array;
  } else {
    var temp = []; //一个临时数组
    //遍历当前数组
    array.forEach(function (value, index) {
      temp.indexOf(value) == -1 ? temp.push(value) : '';
    })
    return temp;
  }
}
// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]复制代码
  • [ES6]内置的for-of方法
function uniqueUseForOf(array) {
  const temp = []; //一个临时数组
  // 传入值必须存在,且长度小于等于1的时候直接返回数组
  if (array && array.length <= 1) {
    return array;
  } else {
    //遍历当前数组
    for (let x of array) {
      temp.indexOf(x) === -1 ? temp.push(x) : '';
    }

  }
  return temp;
}
// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]

进阶版- 包含NaN,undefined,null

测试数组

[1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN']

知识点

  • NaN有两中通用判定方法和数组中一种判定方法:
    • 一个是绝对不全等于(===)自身
    • 一个是ES6isNaN()
    • 数组原型链上的Array.prototype.includes()
  • [ES5]: 不等特性,需要借助占位符
function uniqueUseNotAllEqual(array) {

  var temp = [], //一个临时数组
      mark = true; // 标识位

  //遍历当前数组
  for (var i = 0, j = array.length; i < j; i++) {
    // 标识位的作用就是用来判断是否存在NaN,第一次找到保留到新数组中
    // 然后标识位置改为false是为了再次找到的时候不推入数组
    if (array[i] !== array[i]) {
      // 这里的不等特性,也可以用isNaN判断[ES6]
      mark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
      mark = false;

    } else
      temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';

  }
  return temp;
}

// result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
  • [ES6]内置Array.prototype.includes()大法
function uniqueCompareUseIncludes(array) {
  // 传入值必须存在,且长度小于等于1的时候直接返回数组
  if (array && array.length <= 1) {
    return array;
  } else {
    let temp = []; //一个临时数组
    //遍历当前数组
    for (let x of array) {
      // includes() 方法用来判断当前数组是否包含某指定的值,如果是,则返回 true,否则返回 false。
      temp.includes(x) ? '': temp.push(x) ;
    }
    return temp;
  }
}

// result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
  • [ES6] Array.from或拓展运算符[...]结合Set大法

知识点

  • Set的值具有唯一性,内部会自动===比较,是可迭代对象(iterable),有点特殊的是NaN这货虽然有不全等的特性,在Set里面认为是相同的,所以只能有一个
  • Array.from...可以把类似数组【nodelist or arguments】这类可迭代的对象中转为一个标准的数组
// Array.from + Set的方法
Array.from(new Set([1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN']))
// resule: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]


// ... + Set的方法
[...new Set([1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN'])]
//result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]

高阶版- 包含{},NaN,undefined,null

测试数组

[1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN,{},{},'{}', 0, 1, 'a', 'a', NaN]

知识点

  • {}的比较真心不好做,有残缺性的比较可以这样写 JSON.stringify({}) == '{}'
  • 一个比较完美的方案是借助for in结合原型链的toString来判断
  • [ES5]for-in + call + for方案
function uniqueUseForIn(array) {
  var temp = [];
  var emptyObjectMark = true; // 标识位
  var NaNObjectMark = true; // 标识位
  // 判断空对象,这块判断折腾了许久
  function isEmptyObject(object) {
    if (Object.prototype.toString.call(object) === "[object Object]") {
      for (var i in object) {
        // 存在属性或方法,则不是空对象
        return false
      }
      return true;
    } else {
      return false;
    }
  }

  // 传入值必须存在,且长度小于等于1的时候直接返回数组
  if (array && array.length <= 1) {
    return array;
  } else {
    //遍历当前数组
    for (var i = 0, j = array.length; i < j; i++) {
      // 标识位的作用就是用来判断是否存在NaN和空对象,第一次找到保留到新数组中
      // 然后标识位置改为false是为了再次找到的时候不推入数组
      if (isEmptyObject(array[i])) {
        emptyObjectMark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
        emptyObjectMark = false;
      } else if (array[i] !== array[i]) {
        NaNObjectMark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
        NaNObjectMark = false;
      } else
        temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';

    }
    return temp;

  }
}

// result:[1, "true", true, 5, "F", false, undefined, null, NaN, Object, "{}", 0, "a"]

拓展版:多维数组扁平化再去重;

回应:

留言板的小伙伴说去重深度不够。。2017/5/12

测试数组

[1, 1, [['true', true, true, [5, 'F'], false], undefined], null, null, [undefined, NaN, {}], {}, '{}', 0,1, 'a','a', NaN]

知识点

  • 数组扁平化用了递归实现
  • 代码也考虑了默认参数,防止不传递参数的时候报错
  • [ES5]for-in + call + for + 递归扁平化方案
function uniqueArrayWithFlattern(array) {
  var _array = array || []; // 保存传参
  // 判断空对象,这块判断折腾了许久
  function isEmptyObject(object) {
    if (Object.prototype.toString.call(object) === "[object Object]") {
      for (var i in object) {
        // 存在属性或方法,则不是空对象
        return false
      }
      return true;
    } else {
      return false;
    }
  }



  // 遍历查询判断,扁平化数组
  function forArrayFlattern(arr) {

    for (var m = 0, n = arr.length; m < n; m++) {
      if (Array.isArray(arr[m])) {
        var _tempSpliceArr = _array.splice(m, 1)[0];
        _array = _array.concat(_tempSpliceArr);
        return forArrayFlattern(_array);
      }
    }
    return uniqueArr(_array);
  }


  // 传入值必须存在,且长度小于等于1的时候直接返回数组
  if (_array && _array.length <= 1) {
    return _array
  } else {
    if (Array.isArray(_array)) {
      return forArrayFlattern(_array)
    } else {
      return _array;
    }
  }

  // 数组去重
  function uniqueArr(_array) {
    var temp = [];
    var emptyObjectMark = true; // 标识位
    var NaNObjectMark = true; // 标识位
    console.log(_array.length)
    //遍历当前数组
    for (var a = 0, b = _array.length; a < b; a++) {
      // 标识位的作用就是用来判断是否存在NaN和空对象,第一次找到保留到新数组中
      // 然后标识位置改为false是为了再次找到的时候不推入数组

      console.log(_array[a]);
      if (isEmptyObject(_array[a])) {
        emptyObjectMark && temp.indexOf(_array[a]) == -1 ? temp.push(_array[a]) : '';
        emptyObjectMark = false;
      } else if (_array[a] !== _array[a]) {
        NaNObjectMark && temp.indexOf(_array[a]) == -1 ? temp.push(_array[a]) : '';
        NaNObjectMark = false;
      } else {
        temp.indexOf(_array[a]) === -1 ? temp.push(_array[a]) : '';
      }
    }
    console.log(temp);
    return temp;
  }
}

// result:[1, null, Object, "{}", 0, "a", NaN, undefined, "true", true, false, 5, "F"]
// 用ES6来写的话,应该代码量可以稍微再精简些

总结

相信各位小伙伴把这个弄懂了之后,各种面试中的花样数组去重要求对你来说都不是很坑了;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Yaml 速成查询表

    若是接触过编程数据结构的,对这个上手贼快. 写过json这些的...主要关注下缩进这些,理解下概念即可! 这里只列出非常高频且通用性高的使用姿势,个别骚姿势自行...

    CRPER
  • 编程字体推荐

    对于编码的我们,一个赏心悦目的字体,可以愉悦身心。 以下是我推荐一波等宽字体,有兴趣的可以装一波。

    CRPER
  • ant-design-vue 1.6.2版本,jsx写法下报ant-portal指令异常解决姿势

    CRPER
  • 独家 | 使用Python的OpenAI Gym对Deep Q-Learning的实操介绍(附学习资源)

    即使你不喜欢玩游戏,深度强化学习也很重要。只用看当前使用深度强化学习进行研究的各种功能就知道了:

    数据派THU
  • 聊一聊相等运算与 SameValue

    SameValue 与 SameValueZero 其实是 ecma 中,比较值是否相等的算法,具体参考如下:

    IMWeb前端团队
  • 微信小程序开发应该注意的问题

    小程序是微信主推的产品,现在也有不少公司的产品已经开发了小程序版本。那么,开发小程序你需要了解哪些知识? 1、使用微信web开发者工具 此工具是微信官方提供的小...

    连胜
  • 如何应对混合云网络的复杂性?

    在经过一番艰苦努力的之后,我最终调试解决了一个非常棘手的混合云网络问题。 虚拟私有云(VPC)提供了一个包含免费虚拟机(VM)使用时间的培训项目,学生可以跟随一...

    静一
  • 云应用:混合云需要混合网络来支撑

    在经过一番艰苦努力的之后,我最终调试解决了一个非常棘手的混合云网络问题。 虚拟私有云(VPC)提供了一个包含免费虚拟机(VM)使用时间的培训项目,学生可以跟随一...

    静一
  • 临时表空间故障处理 (33天)

    最近测试环境需要把一些现有的存储空间匀出一部分来给新增的环境使用。 unix组的人很快就空间按照指定的比例重新切分好了。环境交给我的时候,我先把数据库起来,没有...

    jeanron100
  • 前端小知识10点(2020.5.17)

    JS是用双精度浮点数来存储number类型的, 而|是二进制或,会先将number转为整数,再进行位运算,所以可以用来取整

    进击的小进进

扫码关注云+社区

领取腾讯云代金券