前言

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

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

 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. Linux磁盘分区-mount挂载

    Linux磁盘分区类型 磁盘存储术语CHS head:磁头  磁头数=盘面数 track:磁道  磁道=柱面数 sector:扇区,512bytes cylinder:柱面 1柱面=512*secto ...

  2. [源码解析] TensorFlow 分布式之 ParameterServerStrategy V1

    [源码解析] TensorFlow 分布式之 ParameterServerStrategy V1 目录 [源码解析] TensorFlow 分布式之 ParameterServerStrategy ...

  3. 一个登录点两个逻辑漏洞-edusrc

    最近呢, 也是基础漏洞学的差不多了, 就在edusrc上面实战, 刚开始搞一些信息泄漏啥的, 提交了十几个, 结果就他娘的通过了一个. 咱也就不碰信息泄漏了, 没得意思. 关于这个学校测试时也是有坑的 ...

  4. numpy学习Ⅱ

    今天有空再把numpy看一下,补充点不会的,再去看matplotlib 回顾之前笔记,发现之前的numpy学习Ⅰ中关于numpy的行.列.维可能表述有点不清晰,这里再叙述一下 import numpy ...

  5. 5分钟了解二叉树之AVL树

    转载请注明出处:https://www.cnblogs.com/morningli/p/16033733.html AVL树是带有平衡条件的二叉查找树,其每个节点的左子树和右子树的高度最多相差1.为了 ...

  6. linux篇-Linux逻辑卷详解总结

    LVM是逻辑卷管理(Logical Volume Manager)的简称,它是建立在物理存储设备之上的一个抽象层,允许你生成逻辑存储卷,与直接使用物理存储在管理上相比,提供了更好灵活性. LVM将存储 ...

  7. Hibernate基础入门2

    HQL与Criteria HQL(Hibernate Query Language)-官方推荐面向对象的查询语言,与SQL不同,HQL中的对象名是区分大小写的(除了JAVA类和属性其他部分不区分大小写 ...

  8. P4169 [Violet]天使玩偶

    两种操作:1.加入点(x,y); 2.查询距(x,y)最近的点的曼哈顿距离距离 思路:绝对值拆开通常可以取max,不过这里直接分类讨论4种情况,我们发现如果找\(i\)点左下点\(j\)\((x_j& ...

  9. 关于『进击的Markdown』:第二弹

    关于『进击的Markdown』:第二弹 建议缩放90%食用 众里寻他千百度,蓦然回首,Markdown却在灯火灿烂处 MarkdownYYDS! 各位早上好!  我果然鸽稿了  Markdown 语法 ...

  10. git bisect:让你闭眼都能定位疑难 bug的利器

    摘要:git bisect命令使用二分搜索算法来查找提交历史中的哪一次提交引入了错误.它几乎能让你闭着眼睛快速定位任何源码导致的问题,非常实用. 本文分享自华为云社区<利用好 git bisec ...