关于JavaScript数组去重的一些理解汇总


Posted in Javascript onSeptember 10, 2020

前言

做前端开发几年,在项目中用到数组去重的机会倒不是很多,但是在面试的时候却经常被问到,个人理解,这道题真正考的是对JavaScript的基础的掌握,因为有很多种方式可以做到。这次就根据这道题,将相关的知识理解透彻。

一、ES6中的new Set方式

先看看MDN上对Set的描述:

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

关键字:任何类型都可以存储、存储进来的值都是唯一的,这样就可以做到先把数组中重复的数据去掉。

const list1 = [1, 2, 3, 7, 11, 56, 3, 2, 4, 5]
const list2 = new Set(list1)
console.log(list2)

看一下返回的结果:

关于JavaScript数组去重的一些理解汇总

由结果可知,返回了一个可迭代的Set对象,此时还需要把Set对象转为数组。此时可以用到
Array.from()。

**Array.from()** 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例.

刚好new Set()返回的就是一个可迭代的对象。

const list3 = Array.from(new Set([null, null, undefined, 12, undefined, 7, 2, 1, 22, 2, function a(){}, function a(){}, {}, {}]))

结果是返回了一个数组:

关于JavaScript数组去重的一些理解汇总

这种方式不考虑兼容性,且去不掉重复的function和{}

关于Array.from()的扩展

Array.from(arrayLike[, mapFn[, thisArg]])

arrayLike: 伪数组和可迭代对象

mapFn:每个元素会执行这个回调方法

thisArg: 执行回调函数时this的指向。

const list4 = Array.from('name', arg => arg + 12)
console.log(list4) // [ 'n12', 'a12', 'm12', 'e12' ]

伪数组对象(拥有一个 length 属性和若干索引属性的任意对象, 如string)

可迭代对象(可以获取对象中的元素,如 Map和 Set 等)

二、set的另一种写法

const list20 = [5, 3, 5, 5, 6, 37, 22, 22]
console.log([...new Set(list20)])

其实跟第一种差不多

三、嵌套的for循环

const list5 = [null, null, undefined, undefined, {}, {}, function q() {}, function q() {}, 34, 2, 1, 2]
for(let i = 0; i < list3.length; i++) {
  for (let j = i + 1; j < list3.length; j++) {
    if (list3[i] === list3[j]) {
      list3.splice(j, 1) // 将重复的数据删掉一个
      j-- // 因为删除掉了一个元素,就从这个元素的索引重新开始
    }
  }
}

这种方式用了splice(index, num)的方法,返回的结果也没有把function和对象给去掉。我们能想到的最简单的方式就是这种去重方式。

四、indexOf方式

const list6 = [null, null, undefined, undefined, NaN, NaN, false, 'false', function a(){}, function a() {}, {}, {}]
const list7 = []
for(let k = 0; k<list6.length; k++) {
  if (list7.indexOf(list6[k]) === -1) {
    list7.push(list6[k])
  }
}
console.log(list7, 'list7') 
// [null, undefined,NaN, NaN,false,'false',[Function: a], [Function: a],{},{}] list7

关于indexOf

Array构造函数和String构造函数的原型上都有这个方法

MDN上对他们的解释分别是:

**indexOf()**方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

arr.indexOf(searchElement[, fromIndex])

**indexOf()** 方法返回调用它的 String 对象中第一次出现的指定值的索引,从 fromIndex 处进行搜索。如果未找到该值,则返回 -1。

str.indexOf(searchValue [, fromIndex])

这么一看 就知道他俩用法一样。

五、sort

先看看关于sort的解释:

**sort()** 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。

所以这种方式,在数组长的时候可能保证不了性能。用它去重的思路是什么呢?先用sort排序,然后前一个跟后一个比较,这样相同的值永远都是邻关系。

const list8 = [null, null, undefined, undefined, NaN, NaN, false, 'false', function a(){}, function a() {}, {}, {}]
const list9 = list8.sort()
const list10 = [list9[0]]
for(let i = 1; i<list9.length; i++) {
  if (list9[i] !== list9[i-1]) {
    list10.push(list9[i])
  }
}

这一看就很容易理解。

六、includes

const list11 = [null, null, undefined, undefined, NaN, NaN, false, 'false', function a(){}, function a() {}, {}, {}]
const list12 = []
for (let i = 0; i<list11.length ; i++) {
  if (!list12.includes(list11[i])) {
    list12.push(list11[i])
  }
}

此结果没有把function和{}去重,其他的都去重了。

includes也是Array构造函数和String构造函数上都有的方法。

七、filter和indexOf

// 数组去重方式6-filter和indexOf
const list13 = [1, 2, 3, 3, 5]
const list14 = list13.filter((item, index, arr) => {
  // 也就是在遍历这个数组list13的时候,每次拿当前的元素跟此数组中这个元素第一次出现的位置相比,如果位置是一致的,就返回当前元素
  return list13.indexOf(item, 0) === index
})

八、递归

const list15 = [1, 2, 3, 4, 5, 7, 6, 4, 3]
const list16 = list15
const len = list16.length
list16.sort(function(a, b) { return a - b }) // 先排序
function loop(index) {
  if (index >= 1) {
    if (list16[index] === list16[index - 1]) {
      list16.splice(index, 1)
    }
  loop(index - 1)
  }
}
loop(len - 1)

关于sort排序

在排序的时候 我们可能忽略了一个问题,可能得到的并不是我们想要的结果,比如下面的例子:

const list17 = [22, 1, 2, 15, 3, 4, 3, 1, 11]
console.log(list17.sort()) // [1, 1, 11, 15, 2, 22, 3, 3, 4]

看看得到的结果,根本就不是按照从小到大排的。这是因为:默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
所以 要另找一种排序的方法,就是给sort传入一个函数。

arr.sort([compareFunction])

参数:

  • compareFunction: 可传可不传,传的话用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。
  • compareFunction的参数1-firstEl: 用于比较的第一个元素
  • compareFunction的参数2-secondEl: 用于比较的第二个元素
  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本);
  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
  • compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

返回值:

返回的是已经原地排序的数组。

九、用Map数据结构排序

const list 18 = [2, 3, 33, 2, 5, 1, 3]
const map1 = new Map()
for(let i = 0; i<list18.length; i++) {
  if (map1.get(list18[i])) {
    map1.set(list18[i], true)
  } else {
    map1.set(list18[i], false)
    list19.push(list18[i])
  }
}

这个比较好理解, 就不多说了。

总结

到此这篇关于关于JavaScript数组去重的一些理解的文章就介绍到这了,更多相关JavaScript数组去重内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery EasyUI API 中文文档 - NumberSpinner数值微调器使用介绍
Oct 21 Javascript
Lazy Load 延迟加载图片的jQuery插件中文使用文档
Oct 18 Javascript
js 将json字符串转换为json对象的方法解析
Nov 13 Javascript
jQuery 无限级菜单的简单实例
Feb 21 Javascript
ie浏览器使用js导出网页到excel并打印
Mar 11 Javascript
js实现搜索框关键字智能匹配代码
Mar 26 Javascript
Node.js 文件夹目录结构创建实例代码
Jul 08 Javascript
js获取腾讯视频ID的方法
Oct 03 Javascript
JS禁止查看网页源代码的实现方法
Oct 12 Javascript
vue-cli的工程模板与构建工具详解
Sep 27 Javascript
JS实现导航栏楼层特效
Jan 01 Javascript
vue中使用mockjs配置和使用方式
Apr 06 Vue.js
vue中jsonp插件的使用方法示例
Sep 10 #Javascript
vue 实现一个简单的全局调用弹窗案例
Sep 10 #Javascript
vue v-for 点击当前行,获取当前行数据及event当前事件对象的操作
Sep 10 #Javascript
vue 使用lodash实现对象数组深拷贝操作
Sep 10 #Javascript
Vue 按照创建时间和当前时间显示操作(刚刚,几小时前,几天前)
Sep 10 #Javascript
vue 导出文件,携带请求头token操作
Sep 10 #Javascript
vue radio单选框,获取当前项(每一项)的value值操作
Sep 10 #Javascript
You might like
php中spl_autoload详解
2014/10/17 PHP
Yii框架引用插件和ckeditor中body与P标签去除的方法
2017/01/19 PHP
ThinkPHP模板标签eq if 中区分0,null,false的方法
2017/03/24 PHP
PHP框架Laravel中实现supervisor执行异步进程的方法
2017/06/07 PHP
ThinkPHP框架实现的邮箱激活功能示例
2018/06/15 PHP
Apache+PHP+MySQL搭建PHP开发环境图文教程
2020/08/06 PHP
Jquery操作Select 简单方便 一个js插件搞定
2009/11/12 Javascript
Zero Clipboard js+swf实现的复制功能使用方法
2010/03/07 Javascript
关于JavaScript中原型继承中的一点思考
2012/07/25 Javascript
Javascript代码在页面加载时的执行顺序介绍
2013/05/03 Javascript
总结Javascript中的隐式类型转换
2016/08/24 Javascript
Bootstrap基本组件学习笔记之下拉菜单(7)
2016/12/07 Javascript
javascript 正则表达式分组、断言详解
2017/04/20 Javascript
微信小程序slider组件使用详解
2018/01/31 Javascript
微信小程序实现页面下拉刷新和上拉加载功能详解
2018/12/03 Javascript
vue工程全局设置ajax的等待动效的方法
2019/02/22 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
vue如何实现动态加载脚本
2020/02/05 Javascript
js+canvas绘制图形验证码
2020/09/21 Javascript
[03:08]Ti4观战指南上
2014/07/07 DOTA
Python中用函数作为返回值和实现闭包的教程
2015/04/27 Python
500行代码使用python写个微信小游戏飞机大战游戏
2019/10/16 Python
Python使用Tkinter实现滚动抽奖器效果
2020/01/06 Python
Html5 audio标签样式的修改
2016/01/28 HTML / CSS
中国领先的专业家电网购平台:国美在线
2016/12/25 全球购物
联想智利官方网站:Lenovo Chile
2020/06/03 全球购物
Ejb技术面试题
2015/04/29 面试题
党员批评与自我批评(5篇)
2014/09/23 职场文书
2014年社区民政工作总结
2014/12/02 职场文书
店铺转让协议书
2014/12/02 职场文书
告知书格式
2015/07/01 职场文书
励志正能量20句:送给所有为梦想拼搏的人
2019/11/11 职场文书
MySQL 主从复制数据不一致的解决方法
2022/03/18 MySQL
SpringBoot中使用Redis作为全局锁示例过程
2022/03/24 Java/Android
SpringBoot全局异常处理方案分享
2022/05/25 Java/Android
MySQL解决Navicat设置默认字符串时的报错问题
2022/06/16 MySQL