前言

数组去重在面试和工作中都是比较容易见到的问题。

这篇文章主要是来测试多个方法,对下面这个数组的去重结果进行分析讨论。如果有不对的地方,还请大家指出。

 const arr = [ 1, 1, "1", "1", 0, 0, "0", "0", true, false, "true", "false", "a", "A", undefined, undefined, "undefined", null, null, 'null', NaN, NaN, +0, -0, new String("1"), new String("1"), Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], [] ];

特殊类型

console.log(1 == "1"); // true
console.log(1 === "1"); // false console.log(0 == "0"); // true
console.log(0 === "0"); // false console.log(0 == +0); // true
console.log(0 === +0); // true console.log(0 == -0); // true
console.log(0 === -0); // true console.log(+0 == -0); // true
console.log(+0 === -0); // true console.log(0 == false); // true
console.log(0 === false); // false console.log(0 == undefined); // false
console.log(0 === undefined); // false console.log(0 == null); // false
console.log(0 === null); // false console.log(1 == true); // true
console.log(1 === true); // false console.log(undefined == null); // true
console.log(undefined === null); // false console.log(NaN == NaN); // false
console.log(NaN === NaN); // false console.log(new String("1") == new String("1")); // false
console.log(new String("1") === new String("1")); // false
Object.prototype.toString.call(new String('1')) // '[object String]' console.log(/a/ == /a/); // false
console.log(/a/ === /a/); // false
Object.prototype.toString.call(/a/); //'[object RegExp]' console.log(Symbol(1) == Symbol(1)); // false
console.log(Symbol(1) === Symbol(1)); // false console.log({} == {}); // false
console.log({} === {}); // false console.log([] == []); // false
console.log([] === []); // false

接下来,我们看看下面多个去重方法,对以上特殊类型的去重效果。

代码一(暴力解法)

// 暴力解法一

function unique(array) {
if (!Array.isArray(array)) {
console.log("type error!");
return;
}
const res = [array[0]];
let arrLen = array.length;
let resLen = res.length; for (let i = 0; i < arrLen; i++) {
let flag = true;
for (let j = 0; j < resLen; j++) {
if (array[i] === res[j]) {
flag = false;
break;
}
}
if (flag) {
res.push(array[i]);
resLen = res.length;
}
}
return res;
}
// [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. NaN不去重
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol(1)不去重

暴力解法,简单易理解,兼容性好。去重结果如上所示。

代码二(ES6)

// ES6 Array.from + Set 方法一
function unique(array) {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
return Array.from(new Set(array))
} // ES6 点运算 + Set 方法二
function unique1(array) {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
return [...new Set(arr)]
} // ES6 箭头函数 + 点运算 + Set 方法三
const unique2 = (array) => {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
return [...new Set(arr)]
} // ES6 Map + ES5 filter 方法四
function unique3(array) {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
const seen = new Map()
return array.filter((a) => !seen.has(a) && seen.set(a, 1))
}

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. 去重NaN
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol(1)不去重

代码三(indexOf + forEach)

利用indexOf检测元素在新数组是否存在

// indexOf + forEach 利用indexOf检测元素在新数组是否存在
function unique(array) {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
const newArr = [];
array.forEach((el) => {
if (newArr.indexOf(el) === -1) {
newArr.push(el);
}
});
return newArr;
}

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. NaN不去重
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol(1)不去重

代码四(indexOf + filter)

利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等

// indexOf + forEach 利用indexOf检测元素在新数组是否存在
function unique(array) {
if (!Array.isArray(array)) {
console.log('type error!')
return
}
return array.filter((item, index) => {
return array.indexOf(item) === index;
});
} console.log([NaN].indexOf(NaN)); // -1

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. 两个NaN都会被删除
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol(1)不去重

重点:

console.log([NaN].indexOf(NaN)); // -1

代码五(sort排序,不支持Symbol)

sort()方法主要是用于对数组进行排序,默认情况下该方法是将数组元素转换成字符串,然后按照ASC码进行排序

// sort()方法不支持Symbol,Symbol不支持转换成字符串
function unique(array) {
if (!Array.isArray(array)) {
console.log("type error!");
return;
}
const sortArr = array.sort();
const newArr = [];
sortArr.forEach((el, i) => {
if (sortArr[i] !== sortArr[i - 1]) {
newArr.push(el);
}
});
return newArr;
}

输出:

[[], [], /a/, /a/, 0, "0", 0, 1, "1", String {'1'}, String {'1'}, "A", NaN, NaN, {}, {}, "a", false, "false", null, "null", true, "true", "undefined", undefined]

输出结果说明:

  1. +0-00"0"位置不同会导致去重不了
  2. NaN不去重
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. sort()方法不支持处理含有Symbol的数组

代码六(includes)

利用includes()方法检查新数组是否包含原数组的每一项

// 利用includes()方法检查新数组是否包含原数组的每一项
function unique(array) {
if (!Array.isArray(array)) {
console.log("type error!");
return;
} const newArr = [];
array.forEach((el) => {
newArr.includes(el) ? newArr : newArr.push(el);
});
return newArr;
}

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. 去重NaN
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol不去重

代码七(includes+reduce)

利用includes()方法检查新数组是否包含原数组的每一项

// 利用includes()方法检查新数组是否包含原数组的每一项
function unique(array) {
if (!Array.isArray(array)) {
console.log("type error!");
return;
} return array.reduce((pre, cur) => {
!pre.includes(cur) && pre.push(cur);
return pre;
}, []);
}

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]

输出结果说明:

  1. 去重+0-00
  2. 去重NaN
  3. 对象new String("1")/a/{}不去重
  4. 数组[]不去重
  5. Symbol不去重

代码八(对象key)

利用了对象的key不可以重复的特性来进行去重

// 利用了对象的key不可以重复的特性来进行去重
function unique(array) {
if (!Array.isArray(array)) {
console.log("type error!");
return;
} const obj = {};
const newArr = [];
array.forEach((val) => {
if (!obj[typeof val + JSON.stringify(val)]) {
// 将对象序列化之后作为key来使用
obj[typeof val + JSON.stringify(val)] = 1;
newArr.push(val);
}
});
return newArr;
}

输出:

[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, Symbol(1), {}, []]

输出结果说明:

  1. 去重+0-00
  2. 去重NaN
  3. 去重对象new String("1"){};两个/a/全部被删除了
  4. 去重数组[]
  5. 去重Symbol

将不该去重的Symbol去重了;将两个/a/全部删除了

总结

方法 结果 说明
for循环暴力解法 [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] 1.去重+0、-0、0; 2.NaN不去重;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
ES6解法 [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], [] 1.去重+0、-0、0; 2.去重NaN;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
indexOf + forEach [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] 1.去重+0、-0、0; 2.NaN不去重;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
indexOf + filter [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] 1.去重+0、-0、0; 2.两个NaN都会被删除;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
sort排序,不支持Symbol [[], [], /a/, /a/, 0, "0", 0, 1, "1", String {'1'}, String {'1'}, "A", NaN, NaN, {}, {}, "a", false, "false", null, "null", true, "true", "undefined", undefined] 1.+0、-0、0、"0"位置不同会导致去重不了 2.NaN不去重;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.sort()方法不支持处理含有Symbol的数组;
includes [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] 1.去重+0、-0、0; 2.去重NaN;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
includes+reduce [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] 1.去重+0、-0、0; 2.去重NaN;3.对象new String("1")、/a/、{}不去重;4.数组[]不去重;5.Symbol(1)不去重;
对象key [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, Symbol(1), {}, []] 1.去重+0、-0、0; 2.去重NaN;3.去重对象new String("1")、{};两个/a/全部被删除了;4.去重数组[];5.去重Symbol

上面只是简单结果的去重总结,具体的去重选择还需要根据我们业务场景来选择去重方法。

演示地址

可以去Github仓库查看演示代码

跟着大佬学系列

主要是日常对每个进阶知识点的摸透,跟着大佬一起去深入了解JavaScript的语言艺术。

后续会一直更新,希望各位看官不要吝啬手中的赞。

感谢各位的支持!!!

如果有错误或者不严谨的地方,请务必给予指正,十分感谢!!!

喜欢或者有所启发,欢迎 star!!!

参考

原文地址

【跟着大佬学JavaScript】之数组去重(结果对比)

【跟着大佬学JavaScript】之数组去重(结果对比)的更多相关文章

  1. 【跟着大佬学JavaScript】之节流

    前言 js的典型的场景 监听页面的scroll事件 拖拽事件 监听鼠标的 mousemove 事件 ... 这些事件会频繁触发会影响性能,如果使用节流,降低频次,保留了用户体验,又提升了执行速度,节省 ...

  2. 【跟着大佬学JavaScript】之lodash防抖节流合并

    前言 前面已经对防抖和节流有了介绍,这篇主要看lodash是如何将防抖和节流合并成一个函数的. 初衷是深入lodash,学习它内部的好代码并应用,同时也加深节流防抖的理解.这里会先从防抖开始一步步往后 ...

  3. JavaScript 实现数组去重

    JavaScript 实现数组去重 演示Demo 数组去重 // 方法一 function unique1(a) { var res = []; for (var i = 0, len = a.len ...

  4. JavaScript中数组去重的几种方法

    JavaScript中数组去重的几种方法 正常情况下,数据去重的工作一般都是由后端同事来完成的,但是前端也要掌握好处理数据的能力,万一去重的工作交给我们大前端处理,我们也不能怂呀.现在我总结了一些去重 ...

  5. JavaScript实现数组去重方法

    一.利用ES6 Set去重(ES6中最常用) function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,'tru ...

  6. JavaScript之数组去重

    前言:昨天看到了别人发的帖子,谈到了面试题中经常出现的数组去重的问题.作为一个热爱学习.喜欢听老师话的好孩纸,耳边忽然想起来高中老师的谆谆教导:不要拿到题就先看答案,要先自己思考解答,然后再对照答案检 ...

  7. 使用JavaScript进行数组去重——一种高效的算法

    最近比较忙,没时间更新博客,等忙完这阵子会整理一篇使用AngularJS构建一个中型的单页面应用(SPA)的文章,尽情期待!先占个坑. 数组去重的算法有很多种,以下是一种. 思路如下: 定义一个空的对 ...

  8. JavaScript 高性能数组去重

    中午和同事吃饭,席间讨论到数组去重这一问题 我立刻就分享了我常用的一个去重方法,随即被老大指出这个方法效率不高 回家后我自己测试了一下,发现那个方法确实很慢 于是就有了这一次的高性能数组去重研究 一. ...

  9. Javascript作业—数组去重(要求:原型链上添加函数)

    数组去重(要求:原型链上添加函数) <script> //数组去重,要求:在原型链上添加函数 //存储不重复的--仅循环一次 if(!Array.prototype.unique1){ A ...

随机推荐

  1. sqlx操作MySQL实战及其ORM原理

    sqlx是Golang中的一个知名三方库,其为Go标准库database/sql提供了一组扩展支持.使用它可以方便的在数据行与Golang的结构体.映射和切片之间进行转换,从这个角度可以说它是一个OR ...

  2. 容器内的Linux诊断工具0x.tools

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 Linux上有大量的问题诊断工具,如perf.bcc等,但这些诊断工具,虽然功能强大,但却需要很高的权限才可以使用 ...

  3. 关键字 global和nonlocal

    globale 表示从全局把一个变量(比如a)引入局部,后面的变量全是此变量a 使用   globale 变量名 # 全局变量一般是不能随意的修改的 # a = 10 # def func(): # ...

  4. ansible的介绍与安装

    ansible是在远程主机上批量执行命令或者脚本的一个工具 epel源:一个第三方的yum源(阿里巴巴开源镜像站:https://opsx.alibaba.com/mirror?lang=zh-CN) ...

  5. mybatis 查询返回的类型中字段类型为 List<xx>

    基本类型数组 mapper.xml <resultMap id="xxDtoResultMap" type="com.xx.xxDto"> < ...

  6. 105_Power Pivot财务科目(层级深度&筛选深度)

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 在财务科目中,需要按照科目层级来显示:在excel中都是用公式来实现,而且对于数据的管理及更新是一件头痛的事情, ...

  7. Border性质习题与证明

    KMP 第一次接触 \(border\) 都是先从 KMP 开始的吧. 思想在于先对于一个串自匹配以求出 fail 指针(也就是 border) 然后就可以在匹配其他串的时候非常自然的失配转移.在此顺 ...

  8. Spring bean到底是如何创建的?(上)

    前言 众所周知,spring对于java程序员来说是一个及其重要的后端框架,几乎所有的公司都会使用的框架,而且深受广大面试官的青睐.所以本文就以常见的一个面试题"spring bean的生命 ...

  9. JavaScript Object学习笔记一

    Object.assign(target, source1, source2, ...)//用于对象的复制合并(同名属性后覆盖前)或拷贝(拷贝自身可枚举属性,不拷贝继承属性或不可枚举属性),将sour ...

  10. 我所使用的生产 Java 17 启动参数

    JVM 参数升级提示工具:jacoline.dev/inspect JVM 参数词典:chriswhocodes.com Revolut(英国支付巨头)升级 Java 17 实战:https://ww ...