JavaScript常用数组去重的方法及对比详解

 更新时间:2022年07月21日 08:44:34   作者:易函123  
数组去重在面试和工作中都是比较容易见到的问题。这篇文章主要是来测试多个方法,对下面这个数组的去重结果进行分析讨论,需要的可以参考一下

前言

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

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

 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/, [], []]

输出结果说明:

  • 去重+0-00
  • NaN不去重
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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/, [], []]

输出结果说明:

  • 去重+0-00
  • 去重NaN
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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/, [], []]

输出结果说明:

  • 去重+0-00
  • NaN不去重
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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/, [], []]

输出结果说明:

  • 去重+0-00
  • 两个NaN都会被删除
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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]

输出结果说明:

  • +0-00"0"位置不同会导致去重不了
  • NaN不去重
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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/, [], []]

输出结果说明:

  • 去重+0-00
  • 去重NaN
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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/, [], []]

输出结果说明:

  • 去重+0-00
  • 去重NaN
  • 对象new String("1")/a/{}不去重
  • 数组[]不去重
  • 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), {}, []]

输出结果说明:

  • 去重+0-00
  • 去重NaN
  • 去重对象new String("1"){};两个/a/全部被删除了
  • 去重数组[]
  • 去重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常用数组去重的方法及对比详解的文章就介绍到这了,更多相关JavaScript数组去重内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Bootstrap基本组件学习笔记之进度条(15)

    Bootstrap基本组件学习笔记之进度条(15)

    这篇文章主要为大家详细介绍了Bootstrap基本组件学习笔记之进度条,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • js自动生成的元素与页面原有元素发生堆叠的解决方法

    js自动生成的元素与页面原有元素发生堆叠的解决方法

    js自动生成的元素与页面原有元素发生堆叠通过去除浮动,给原有元素(商品扩展信息部分)加上clear:both; 果然正常了
    2013-10-10
  • 跟我学Nodejs(三)--- Node.js模块

    跟我学Nodejs(三)--- Node.js模块

    这是本系列的第三篇文章了,前面2篇网友们反馈回来不少的消息,加上最近2天比较忙,一直没来得及整理,周末了,赶紧给大家整理下发出来,本文讲的是node.js模块
    2014-05-05
  • ES6记录异步函数的执行时间详解

    ES6记录异步函数的执行时间详解

    在这篇文章里,我会实现一个可重用的函数来处理 JavaScript 延时异步操作。有需要的小伙伴们可以参考借鉴,下面来一起看看。
    2016-08-08
  • eval的两组性能测试数据

    eval的两组性能测试数据

    最近对eval火爆的讨论,教主 @Franky 和 灰大 @otakustay 也给了精彩的数据分析,刚好之前也做过类似的测试,我也跟风凑个热闹,提供两组数据供大家参考
    2012-08-08
  • JS删除数组中某个元素的四种方式总结

    JS删除数组中某个元素的四种方式总结

    js删除指定元素方法有很多,下面这篇文章主要给大家介绍了关于JS删除数组中某个元素的四种方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • 基于JavaScript 性能优化技巧心得(分享)

    基于JavaScript 性能优化技巧心得(分享)

    下面小编就为大家分享一篇基于JavaScript 性能优化技巧心得,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • 控制台报错:Cannot access 'xxx' before initialization解决方法

    控制台报错:Cannot access 'xxx' before initializatio

    这篇文章主要给大家介绍了关于控制台报错:Cannot access 'xxx' before initialization的解决方法,文中通过代码介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • js获取Element元素的四种常用方法

    js获取Element元素的四种常用方法

    本文主要介绍了js获取Element元素的四种常用方法,通过介绍getElementById、getElementsByClassName、getElementsByTagName和querySelector等方法,帮助读者了解如何通过不同的方式获取页面中的元素,感兴趣的可以了解一下
    2023-08-08
  • JS合并两个数组的方法详解

    JS合并两个数组的方法详解

    这篇文章主要详细介绍了JS合并两个数组的方法,文中有详细的代码示例,对我们学习JS有一定的帮助,感兴趣的同学可以参考一下
    2023-06-06

最新评论