【 js 算法类】这么全的数组去重,你怕不怕?
以 var arr = [1,2,3,1]; 作为测试用例
方法一:双循环 (时间复杂度比较高,性能一般。)
A、(1)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     var isRepeat;
     for(var i=0; i<len; i++) {  //第一次循环
         isRepeat = false;
         for(var j=i+1; j<len; j++) {  //第二次循环
             if(arr[i] === arr[j]){
                 isRepeat = true;
                 break;
             }
         }
         if(!isRepeat){
             newArr.push(arr[i]);
         }
     }
     return newArr;
 } 
输出 newArr 结果:
B、(2)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     for(var i=0; i<len; i++){ // 第一次循环
         for(var j=i+1; j<len; j++){ // 第二次循环
             if(arr[i] === arr[j]){
                 j = ++i;
             }
         }
         newArr.push(arr[i]);
     }
     return newArr;
 }
输出 newArr 结果:
tip: j = ++ i; 等价于 j = j+1; i = i+1;
整体思路就是 如果是重复元素,则跳过重复元素,不对其进行 push 操作。
方法二:Array.prototype.indexOf()
A、(3)
 function unique(arr) {
     return arr.filter(function(item, index){ //item 表示数组中的每个元素,index 是每个元素的出现位置。
        return arr.indexOf(item) === index; // indexOf 返回第一个索引值
    });
 }
输出 新 arr 结果:
tip:var new_arrary = arr.filter(callback[, thisArg]);  其中 callback 用来测试数组的每个元素的函数。调用时使用参数 element, index, array。
返回true表示保留该元素(通过测试),false则不保留。thisArg可选,执行 callback 时的用于 this 的值。 
整体思路就是索引不是第一个索引,说明是重复值。
B、(4)
 function unique(arr) {
     var newArr = [];
     arr.forEach(function(item){ //一次循环,item 即为数组中的每一项
         if(newArr.indexOf(item) === -1){
             newArr.push(item);
         }
     });
     return newArr;
 }
输出 newArr 结果:
方法三:Array.prototype.sort()
A、(5)
 function unique(arr) {
     var newArr = [];
     arr.sort();
     for(var i = 0; i < arr.length; i++){
         if( arr[i] !== arr[i+1]){
             newArr.push(arr[i]);
         }
     }
     return newArr;
 }      
输出 newArr 结果:
tip: 整体思路就是 先进行排序,然后比较相邻元素。
B、(6)
 function unique(arr) {
   var newArr = [];
   arr.sort();
   var newArr = [arr[0]];
   for(var i = 1; i < arr.length; i++){
     if(arr[i] !== newArr[newArr.length - 1]){
      newArr.push(arr[i]);
     }
   }
   return newArr;
 }
输出 newArr 结果:
tip:整体思路就是 先进行排序,将原数组中的第一个元素复制给结果数组,然后检查原数组中的第 i 个元素 与 结果数组中的最后一个元素是否相同。
方法四:使用对象key来去重 (7)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     var tmp = {};
     for(var i=0; i<len; i++){
         if(!tmp[arr[i]]){
             tmp[arr[i]] = 1;
             newArr.push(arr[i]);
         }
     }
     return newArr;
 }
输出 newArr 结果:
tip:整体思路就是 利用了对象(tmp)的 key 不可以重复的特性来进行去重。 但是会出现如下问题需要注意:
1、无法区分隐式类型转换成字符串后一样的值,比如 1 和 '1' 。
2、无法处理复杂数据类型,比如对象(因为对象作为key会变成[object Object])。
3、特殊数据,比如 '__proto__' 会挂掉,因为 tmp 对象的 __proto__ 属性无法被重写。
针对以上问题,解决办法有:
解决问题一、三:可以为对象的 key 增加一个类型,或者将类型放到对象的value中来解决:(8)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     var tmp = {};
     var tmpKey;
     for(var i=0; i<len; i++){
         tmpKey = typeof arr[i] + arr[i];
         console.log(tmpKey); 
         if(!tmp[tmpKey]){
             tmp[tmpKey] = 1;
             newArr.push(arr[i]);
         }
     }
     return newArr;
 }
   输出 newArr 结果:
tip: 代码中第 9 行 console 出来的东西 如图:
          
解决问题二:可以将对象序列化之后作为key来使用。这里为简单起见,使用JSON.stringify()进行序列化。(9)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     var tmp = {};
     var tmpKey;
     for(var i=0; i<len; i++){
         tmpKey = typeof arr[i] + JSON.stringify(arr[i]);
         console.log(tmpKey)
         if(!tmp[tmpKey]){
             tmp[tmpKey] = 1;
             newArr.push(arr[i]);
         }
     }
     return newArr;
 }
   输出 newArr 结果:
tip: 代码中第 9 行 console 出来的东西 如图:
          
看起来和上面一种方法 console 的没有区别,但是将 测试用例 换成一个 对象 var arr = [{xiaoming:23,xiaoqing:45},{xiaoming:24,xiaoqing:45}]; 时,可以看到 console 结果如下:
          
以上都是一些比较普遍的解决办法,下面补充一下 es6 中的方法以及对于 null,NaN,undefined,{} 等类型的去重。
一、es6:
方法一: Map (10)
 function unique(arr) {
     var newArr = [];
     var len = arr.length;
     var tmp = new Map();
     for(var i=0; i<len; i++){
         if(!tmp.get(arr[i])){
             tmp.set(arr[i], 1);
             newArr.push(arr[i]);
         }
     }
     return newArr;
 }
输出 newArr 结果:
tip: Map 是一种新的数据类型,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。可以把它想象成key类型没有限制的对象。它的存取使用单独的get()、set()接口。
方法二:Set (11)
 function unique(arr){
     var set = new Set(arr);
     return Array.from(set);
 }
输出 newArr 结果:
tip:Set 是一种新的数据结构。它类似于数组,但是成员的值都是唯一的,没有重复的值。Array.from() 方法从类似数组或可迭代对象创建一个新的数组实例。
想要具体了解 Set 和 Map ,查看 http://wiki.jikexueyuan.com/project/es6/set-map.html
方法三:Array.prototype.includes() (12)
 function unique(arr) {
     var newArr = [];
     arr.forEach(function(item){
         if(!newArr.includes(item)){
             newArr.push(item);
         }
     });
     return newArr;
 }
输出 newArr 结果:
二、NaN 等类型数据的去重:
使用测试用例,对上面所有算法进行验证 (以 黄色数字 为测试顺序)
 var arr = [1,1,'1','1',0,0,'0','0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/]
 console.log(unique(arr));
得到结果如下:




tips:

That's all ~

学习并感谢:
https://zhuanlan.zhihu.com/p/24753549
http://www.cnblogs.com/fumj/archive/2012/09/09/2677711.html
【 js 算法类】这么全的数组去重,你怕不怕?的更多相关文章
- JS常见的几种数组去重方法
		
总结一下JS中用到的数组去重的方法 方法一: 该方法利用对象的属性值不能相同: function arrDelLikeElement (array) { const result = []; con ...
 - JS 数组去重的几种方式
		
JS 常见的几种数组去重方法 一.最简单方法(indexOf 方法) 实现思路:新建一个数组,遍历要去重的数组,当值不在新数组的时候(indexOf 为 -1)就加入该新数组中: function u ...
 - js引用类型数组去重-对象标记法
		
前言 Js数组去重已经有很多种实现方式:包括逐个检索对比(使用Array.property.indexOf),先排序后对比,使用hash表,利用ES6中的Set()等.这些数组去重办法中速度最快的是h ...
 - js数组去重方法分析与总结
		
数组去重经常被人拿来说事,虽然在工作中不常用,但他能够很好的考察js基础知识掌握的深度和广度,下面从js的不同阶段总结一下去重的方法. ES3阶段 该阶段主要通过循环遍历数组从而达到去重的目的 多次循 ...
 - js面试题之数组去重对比
		
最近看一些面试题,很多都提到了数组去重,用的最多的不外乎就是下面这个例子 arr.filter(function(value,index,arr){ return arr.indexOf(value, ...
 - js数组去重的4种方法
		
js数组去重,老生长谈,今天对其进行一番归纳,总结出来4种方法 贴入代码前 ,先对浏览器Array对象进行支持indexOf和forEach的polyfill Array.prototype.inde ...
 - JS 数组去重(数组元素是对象的情况)
		
js数组去重有经典的 几种方法 但当数组元素是对象时,就不能简单地比较了,需要以某种方式遍历各值再判断是否已出现. 因为: 1.如果是哈希判断法,对象作哈希表的下标,就会自动转换成字符型类型,从而导致 ...
 - js中数组去重的几种方法
		
js中数组去重的几种方法 1.遍历数组,一一比较,比较到相同的就删除后面的 function unique(arr){ ...
 - js数组去重常用方法
		
js数组去重是面试中经常会碰到的问题,无论是前端还是node.js数组常见的有两种形式,一种是数组各元素均为基本数据类型,常见的为数组字符串格式,形如['a','b','c'];一种是数组各元素不定, ...
 
随机推荐
- 关于各种算法以及好的blog的整理(持续更新)
			
一堆博客先扔着,等有空的时候再去看……好像没几个会的…… 以下都是待学习的算法 博弈论 https://www.cnblogs.com/cjyyb/p/9495131.html https://blo ...
 - 前端在js中获取用户所在地区的时间与时区
			
var times = Date() // 如果这种方式不行就使用 New Date() "Sat Jan 05 2019 10:35:24 GMT+0800 (中国标准时间)" ...
 - web端权限维持【好文】
			
前言 关于权限维持,我之前写过一篇基于系统端的后门技术文章,如映像劫持啊,lpk之类. 内容目录: - 构造文件包含漏洞- 隐蔽性脚本木马- 构造sql注入点 正文 0x01 构造文件包含漏洞 > ...
 - Android实战源码--围住神经猫
			
最终效果: AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manif ...
 - Angular使用总结  ---  搜索场景中使用rxjs的操作符
			
在有input输入框的搜索/过滤业务中,总会考虑如何减少发起请求频率,尽量使每次的请求都是有效的.节流和防抖是比较常见的做法,这类函数的实现方式也不难,不过终归还是需要自己封装.rxjs提供了各种操作 ...
 - Smart/400开发上手1:入门
			
1.介绍 Smart/400是在AS/400之上的开发平台,管理开发.运维的全生命周期. 2.设计基础 Introducing Fields Smart通过字段字典Field Dictionary来存 ...
 - Note of The Linux Command Line
			
心得 在用鼠标点击的图形化桌面之前,单纯用键盘操作软件的时代已经很成熟了.并且还在这样延续下去.鼠标不是电脑操作的唯一模式,至少不是程序员的. 在黑色屏幕下,因为没有鼠标所以只能用按键来操作软件.包括 ...
 - 安装SVN并进行汉化的详细步骤
			
安装SVN并进行汉化的详细步骤 SAE提供了不同的代码部署方式,可以分为两类:一是通过SVN客户端部署,这是SAE推荐的代码部署方法.另一个是通过非SVN客户端部署,即在线代码在线编辑器和推荐应用安装 ...
 - MethodImplOptions.Synchronized的一点讨论
			
Review代码发现有一个方法加了[MethodImpl(MethodImplOptions.Synchronized)] 属性,这个属性的目的,从名字上就可以看出,是要对所有线程进行同步执行. 对方 ...
 - System.Threading.Tasks.Task 引起的 IIS 应用池崩溃
			
接口服务运行一段时间后,IIS应用池就会突然挂掉,事件查看日志,会有事件日志Event ID为5011的错误 为应用程序池“PokeIn”提供服务的进程在与 Windows Process Activ ...