JS 多种数组去重方法

简介: JS 多种数组去重方法

无论是在实际开发还是工作面试,数组去重都是一个很常见的问题,今天就来总结一下,以备不时之需。首先声明这篇博客中出现的所有数组去重方法并不是我自己一个人想到的,在撰写这篇博客的期间,参考了网上很多资料,希望能帮到大家。


用户需求:假设有数组 array = [1,5,2,3,4,2,3,1,3,4];要求你要写一个函数 unique(),使得 array.unique() 或 unique(array) 的值为 [1,5,2,3,4],也就是把重复的值都去掉,只保留不重复的值。


1、双重循环方法

双重循环方法是比较简单,而且很容易被想到的方法,具体去重步骤如下:

Array.prototype.unique = function () {    //数组原型上添加 unique 方法
  const newArray = [];
  let isRepeat;    //编辑元素是否重复
  for (let i = 0; i < this.length; i++) {    //外层循环遍历数组
    isRepeat = false;
    for (let j = i + 1; j < this.length; j++) { //内层循环遍历查找重复元素
      if (this[i] === this[j]) {    //如果有相同元素,就跳出当前循环,看下一个元素是否重复
        isRepeat = true;
        break;
      }
    }
    if (!isRepeat) {    //如果不是重复元素,就push到新数组中
      newArray.push(this[i]);
    }
  }
  return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique());    //[2,1,3,5,4]    不稳定

缺点:采用上述这种方式返回的新数组,能做到数组去重的效果,但是并不能反映原数组中元素的出现顺序,不稳定

2、indexOf() 检测元素方法

2.1、方法一:如果索引不是第一个索引,说明是重复值

利用 filter() 方法的过滤功能,indexOf() 方法返回第一个索引值。只将数组中元素第一次出现的返回,之后出现的将被过滤掉

//方法一:
Array.prototype.unique = function () {
  return this.filter((item, index) => {
    return this.indexOf(item) === index;    //数组中每个元素的位置和其第一次出现的下标对应
  })
}
console.log([1,1,2,1,3,4,5,4].unique());    //[1,2,3,4]    稳定

2.2、方法二:如果新数组中含有改元素,说明是重复值

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (newArray.indexOf(item) === -1) {    //如果新数组中不含有当前元素,就push
      newArray.push(item);
    }
  });
  return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique());    //[1,2,3,4]    稳定

上述的这两种方法输出的数组副本,不会将原数组中的元素出现位置打乱,业界俗称:稳定

3、将数组元素先 Sort() 排序

3.1、方法一:先对原数组进行排序,然后再进行元素比较

Array.prototype.unique = function () {
  const newArray = [];
  this.sort();    //排序
  for (let i = 0; i < this.length; i++) {    //对排序数组进行遍历
    if (this[i] !== this[i + 1]) {    //当前元素和后面的元素不相同就push
      newArray.push(this[i]);
    }
  }
  return newArray;
}

3.2、和新数组的最后一个元素进行比较,如果相同就说明是重复元素。

Array.prototype.unique = function () {
  const newArray = [];
  this.sort();    //排序
  for (let i = 0; i < this.length; i++) {
    if (this[i] !== newArray[newArray.length - 1]) {    //和新数组的最后一个元素比较
      newArray.push(this[i]);
    }
  }
  return newArray;
}

上述的这两种方法由于都用到 sort() 方法, 会打乱原有数组的顺序,所以:不稳定

4、检测新数组中元素 includes()

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (!newArray.includes(item)) {    //如果新数组中含有当前元素,就说明是重复值
      newArray.push(item);
    }
  });
  return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique());    //[1,2,3,4]    稳定

5、配合 sort() 使用 reduce() 去重

Array.prototype.unique = function () {
  return this.sort().reduce((init, current) => {
    if(init.length === 0 || init[init.length - 1] !== current){    //类似于3.2方法
      init.push(current);
    }
    return init;
  }, []);
}                //只要涉及到 sort 方法,就是不稳定的

6、Map

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了 “字符串—值” 的对应,Map 结构提供了 “值—值” 的对应,是一种更完善的 Hash 结构实现。


Map 对象保存键值对,并且能够记住键的原始插入顺序。和 Object 类型不同(key 只能是字符串),在 Map 对象中任何值(对象或者原始值) 都可以作为一个键或一个值。详请移步:mdn 文档


使用 Map 对象实现数组去重的主要思路:创建一个空 Map,遍历原始数组,把数组的每一个元素作为 key 存到 Map 对象中,因为 Map 中不会出现相同的 key 值,所以最终得到的 Map 中的所有 key 值就是去重后的结果。

function unique(arr) {
  let hashMap = new Map();   //创建一个新的Map对象
  let result = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(hashMap.has(arr[i])) {     // 判断 hashMap 中是否已有该 key 值
      hashMap.set(arr[i], true);  // 后面的true 代表该 key 值在原始数组中重复了,false反之
    } else {  // 如果 hashMap 中没有该 key 值,添加
      hashMap.set(arr[i], false);  
      result.push(arr[i]);
    }
  } 
  return result;
}
console.log(unique([1, 1, 1, 2, 3, 3, 4, 5, 5, "a", "b", "a"])); // [ 1, 2, 3, 4, 5, 'a', 'b' ]

7、Set

Set 对象允许你存储任何类型的 唯一值,无论是原始值或者是对象引用,该方法天生就适合数组去重。mdn 文档

不成,NaN 和 undefined 都可以被存储在 Set 中, NaN 之间被视为相同的值(NaN被认为是相同的,尽管 NaN !== NaN)

Array.prototype.unique = function () {
  const set = new Set(this);    //new 一个 Set 对象,将数组作为参数传递进去
  return Array.from(set);       //自动实现去重,Array.from() 将其转化为数组
}                               //可以通过 set.add(args) 添加元素
console.log([1,1,2,1,3,4,5,4].unique());    //[1,2,3,4]    稳定
Array.prototype.unique = function () {
  return [...new Set(this)];    //简化上述步骤
}
console.log([1,1,2,1,3,4,5,4].unique());    //[1,2,3,4]    稳定
目录
相关文章
|
6月前
|
监控 负载均衡 JavaScript
有哪些有效的方法可以优化Node.js应用的性能?
有哪些有效的方法可以优化Node.js应用的性能?
356 69
|
5月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法详解
本指南详细介绍在Linux系统中安装和管理Node.js的步骤。首先检查现有环境,包括查看当前版本和清除旧版本;接着通过NodeSource仓库安装最新版Node.js并验证安装结果。推荐使用nvm(Node Version Manager)进行多版本管理,便于切换和设置默认版本。同时,提供常见问题解决方法,如权限错误处理和全局模块迁移方案,以及版本回滚操作,确保用户能够灵活应对不同需求。
488 0
|
5月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法
Debian 11更新Node.js主要就是这三种方式,无论你是初涉其中的新手还是找寻挑战的专家,总有一种方式能满足你的需求。现在,你已经是这个
558 80
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
270 1
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
9月前
|
前端开发 JavaScript
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
400 58
|
7月前
|
JavaScript 前端开发 Java
js 垃圾回收机制的方法
JS回收机制方法讲解
|
8月前
|
JavaScript 前端开发 Java
深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解
Array.find() 是 JavaScript 数组方法中一个非常实用和强大的工具。它不仅提供了简洁的查找操作,还具有性能上的独特优势:返回的引用能够直接影响原数组的数据内容,使得数据更新更加高效。通过各种场景的展示,我们可以看到 Array.find() 在更新、条件查找和嵌套结构查找等场景中的广泛应用。 在实际开发中,掌握 Array.find() 的特性和使用技巧,可以让代码更加简洁高效,特别是在需要直接修改原数据内容的情形。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
8月前
|
存储 JavaScript 前端开发
全网最全情景,深入浅出解析JavaScript数组去重:数值与引用类型的全面攻略
如果是基础类型数组,优先选择 Set。 对于引用类型数组,根据需求选择 Map 或 JSON.stringify()。 其余情况根据实际需求进行混合调用,就能更好的实现数组去重。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
716 62
下一篇
oss云网关配置