数组去重的各种方式对比

数组去重,是一个老生常谈的问题了,在各厂的面试中也会有所提及,接下来就来细数一下各种数组去重的方式吧;

对于以下各种方式都统一命名为 unique,公用代码如下:

// 生成一个包含100000个[0,50000)随机数的数组
var arr = [];
for(var i = 0; i < 100000; i++) {arr.push(Math.floor(Math.random() * 50000));
}
Array.prototype.unique = function() { // 算法 };
console.log(arr.unique());  // 一个已经去重的数组
复制代码

1、双重遍历

双重遍历的实现主要是通过两次遍历的对比,生成一个新的,不含重复数据的数组;

其实现方式有如下两种:

/** 第一种实现方式:* 对数组的每个元素在推入新数组之前与新数组中每个元素比较,如果没有相同值则推入*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [], isRepeat;for(var i = 0, length = this.length; i < length; i++) {isRepeat = false;for(var j = 0, newLength = newArray.length; j < newLength; j++) {if(this[i] === newArray[j]) {isRepeat = true;break;}}if(!isRepeat) newArray.push(this[i]);}return newArray;
};
/** 第二种实现方式* 将数组的每个元素与其后面所有元素做遍历对比,若有相同的值,则不推入新数组,*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [], isRepeat;for(var i = 0, length = this.length; i < length; i++) {isRepeat = false;for(var j = i + 1; j < length; j++) {if(this[i] === this[j]) {isRepeat = true;break;}}if(!isRepeat) newArray.push(this[i]);}return newArray;
};// 实测耗时
// 方式一:2372 ms
// 方式二:4025 ms
复制代码

2、Array.prototype.indexOf()

通过 indexOf 方法查询值在数组中的索引,并通过对索引的判断来实现去重;

主要实现方式有下面两种:

/*** 第一种实现方式* 结合数组的 filter 方法,将相同值的第一个合并到一个新的数组中返回* indexOf 检测到的索引为出现当前值的第一个位置* 若 indexOf 检测到的索引和当前值索引不相等则说明前面有相同的值*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');return this.filter(function(item, index, array) {return array.indexOf(item) === index;});
};
/*** 第二种实现方式* 对数组进行遍历,并将每个元素在新数组中匹配* 若新数组中无该元素,则插入*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [];this.forEach(function(item) {if(newArray.indexOf(item) === -1) newArray.push(item);});return newArray;
};// 实测耗时
// 方式一:3972 ms
// 方式二:2650 ms
复制代码

3、Array.prototype.sort()

sort 方法可对数组进行排序,此时相同的值就会被排到一起,然后通过相邻元素比较就可知是否有相同值了;

举个栗子:

/*** 第一种实现方式* 先将数组通过 sort 排序* 再遍历数组,将每个元素与其前面一个元素比较* 若值不同则表示该元素第一次出现,则插入到新数组*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [];this.sort();for(var i = 0, length = this.length; i < length; i++) {if(this[i] !== this[i - 1]) newArray.push(this[i]);}return newArray;
};
/*** 第二种实现方式* 先将数组通过 sort 排序* 再遍历数组,将每个元素与插入到新数组中的最后一个元素比较* 若值不同则表示该元素第一次出现,则插入到新数组*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [];this.sort();for(var i = 0, length = this.length; i < length; i++) {if(this[i] !== newArray[newArray.length - 1]) newArray.push(this[i]);}return newArray;
};// 实测耗时
// 方式一:105 ms
// 方式二:112 ms
复制代码

由于方式二在每次比较的时候需要重新计算一次 newArray.length 故会稍微比方式一慢一点;

3、Array.prototype.includes(searchElm, fromIndex)

该方法判断数组中是否存在指定元素

参数:

  • searchElm:需要查找的元素
  • fromIndex:开始查找索引位置(若未负值,则从 array.length - fromIndex 位置开始查找

返回值:

  • Boolean:数组中是否存在该元素
/*** 实现方式* 遍历数组,通过 includes 判断每个值在新数组中是否存在* 若不存在,则将值插入到新数组中*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArray = [];this.forEach(function(item) {if(!newArray.includes(item)) newArray.push(item);});return newArray;
};// 实测耗时:2597 ms
复制代码

4、Array.prototype.reduce()

/*** 实现方式* 先将数组进行排序* 然后利用 reduce 迭代和累加的特性,将符合要求的元素累加到新数组并返回*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');return this.sort().reduce(function(newArr, curr) {if(newArr[newArr.length - 1] !== curr) newArr.push(curr);return newArr;}, []);
};// 实测耗时:127 ms
复制代码

5、对象的键值对

利用对象的 key 不能重复的特性来去重; 之前看到有人使用对象的键值对去重的时候,直接将数组的每个值设置为对象的 keyvalue 都为1,每出现一个相同的值时就 value++,这样既可以去重,又可以知道对应的值出现的次数,例如:

var array = ['a', 'b', 'c', 'a', 'a', 'c'];
var newArr = [], obj = {};
array.forEach(function(item) {if(obj[item]) {obj[item]++;} else {obj[item] = 1;newArr.push(item);}
});
console.log(newArr); // ["a", "b", "c"]
console.log(obj);    // {a: 3, b: 1, c: 2}
复制代码

咋一看好像很完美,可是仔细一想,会发现有以下几点原因:

  • 若数组的值中出现了隐式类型转换成字符串后相等的值,则无法区分,例如 '1' 和 1;
  • 若数组中的值有对象,写成 key 之后都是 [object Object],无法区分;

解决方案:

若元素值的类型为 object,则通过 JSON.stringify 方法进行转换;

Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var newArr = [], obj = {};this.forEach(function(item) {if(!obj[typeof item + JSON.stringify(item)]) {obj[typeof item + JSON.stringify(item)] = 1;newArr.push(item);}});return newArr;
};// 实测耗时:142 ms
复制代码

6、Set

Set 对象的特性就是其中的值是唯一的,可利用该特性很便捷的处理数组去重的问题;

/*** 实现方式一* 将数组转换成 Set 对象* 通过 Array.from 方法将 Set 对象转换为数组*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');var set = new Set(this);return Array.from(set);
};// 实测耗时:45 ms/*** 实现方式二* 将数组转换成 Set 对象* 通过 Array.from 方法将 Set 对象转换为数组*/
Array.prototype.unique = function() {if(!Array.isArray(this)) throw new Error('Type Error: The target should be an Array!');return [...new Set(this)];
};// 实测耗时:65 ms
复制代码

由以上耗时结果可以看出:

  • filter, forEach 等方法都会对全数组进行遍历;
  • indexOf, for+break, includes 等方法会对数组遍历,在满足条件的地方跳出遍历

转载于:https://juejin.im/post/5b2612e36fb9a00e50312e2d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux平台Makefile文件的编写基础篇和GCC参数详解

问&#xff1a;gcc中的-I.是什么意思。。。。看到了有的是gcc -I. -I/usr/xxxxx..那个-I.是什么意思呢 最佳答案 答&#xff1a;-Ixxx 的意思是除了默认的头文件搜索路径(比如/usr/include等&#xff09;外&#xff0c;同时还在路径xxx下搜索需要被引用的头文件。 所以你的gcc …

旧知识打造新技术--AJAX学习总结

AJAX是将旧知识在新思想的容器内进行碰撞产生的新技术&#xff1a;推翻传统网页的设计技术。改善用户体验的技术。 学习AJAX之初写过一篇《与Ajax的初次谋面》。当中都仅仅是一些自己浅显的理解&#xff0c;这次就总结一下它在历史长河中的重要地位。 【全】 AJAX全称为Asnychr…

C#数组基本操作

文章目录简介数组排序和反转语法实例查找数组元素语法实例数组元素求和、最大值、最小值、平均值语法实例数组字符串相互转化语法实例在字符串中查找、删除字符数组元素语法实例博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 简介 C#提供了许…

redis(一)--认识redis

Redis官网对redis的定义是&#xff1a;“Redis is an open source, BSD licensed, advanced key-value cache and store”&#xff0c;可以看出&#xff0c;Redis是一种键值系统&#xff0c;可以用来缓存或存储数据。Redis是“Remote Dictionary Server”&#xff08;远程字典服…

转:如何用gcc编译生成动态链接库*.so文件 动态库

转&#xff1a;如何编译.so动态库问&#xff1a;我源文件为main.c, x.c, y.c, z.c,头文件为x.h,y.h,z.h如何编译成.so动态库&#xff1f;编译器用gcc最好能给出详细参数解释&#xff0c;谢谢答&#xff1a;# 声称动代连接库&#xff0c;假设名称为libtest.sogcc x.c y.c z.c -f…

工业镜头的主要参数与选型

文章目录简介1、镜头的分类(1) 以镜头安装分类(2) 以摄像头镜头规格分类(3) 以镜头光圈分类(4) 以镜头的视场大小分类(5) 从镜头焦距上分2、选择镜头的技术依据(1) 镜头的成像尺寸(2) 镜头的分辨率(3) 镜头焦距与视野角度(4) 光圈或通光量3、变焦镜头&#xff08;zoom lens&…

SQLSEVER 中的那些键和约束

SQL Server中有五种约束类型&#xff0c;各自是 PRIMARY KEY约束、FOREIGN KEY约束、UNIQUE约束、DEFAULT约束、和CHECK约束。查看或者创建约束都要使用到 Microsoft SQL Server Managment Studio。1. PRIMARY KEY约束 在表中常有一列或多列的组合&#xff0c;其值能唯一标识表…

数据库 sqlite 进阶

http://www.cppblog.com/czy463/archive/2013/12/16/204816.html 董淳光 前序&#xff1a; Sqlite3 的确很好用。小巧、速度快。但是因为非微软的产品&#xff0c;帮助文档总觉得不够。这些天再次研究它&#xff0c;又有一些收获&#xff0c;这里把我对 sqlite3 的研究列出来&a…

形象的列举-C# 枚举

文章目录简介例子分析点拨博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 简介 枚举类型用于声明一组命名常数。 定义枚举类型语法格式如下&#xff1a;enum 枚举数组名{枚举成员列表};例如&#xff1a; enum week{星期一&#xff0c;星期二…

Confluence 6 手动备份站点

2019独角兽企业重金招聘Python工程师标准>>> Confluence 被配置自动备份数据&#xff0c;使用压缩的 XML 格式。同时你也可以通过 Confluence 的 管理员控制台&#xff08;Administration Console&#xff09;手动进行备份。 你需要具有 System Administrator 权限才…

编写高质量的Makefile

分类&#xff1a; c/c研究 GNU&amp;LINUX2010-09-12 15:31163人阅读 评论(0)收藏举报源地址 &#xff1a;http://acm.hrbeu.edu.cn/forums/index.php?showtopic1827&st0&gopid8924&#entry8924 一、前言 回想自己的第一个Makefile&#xff0c;是这个样子的 …

第六篇:python基础之文件处理

第六篇&#xff1a;python基础之文件处理 阅读目录 一.文件处理流程二.基本操作2.1 文件操作基本流程初探2.2 文件编码2.3 文件打开模式2.4 文件内置函数flush2.5 文件内光标移动2.6 open函数详解2.7 上下文管理2.8 文件的修改一.文件处理流程 打开文件&#xff0c;得到文件句柄…

前端每日实战:56# 视频演示如何用纯 CSS 描述程序员的生活

效果预览 按下右侧的“点击预览”按钮可以在当前页面预览&#xff0c;点击链接可以全屏预览。 https://codepen.io/comehope/pen/YvYVvY 可交互视频 此视频是可以交互的&#xff0c;你可以随时暂停视频&#xff0c;编辑视频中的代码。 请用 chrome, safari, edge 打开观看。 ht…

从特殊到一般-C#中的类

文章目录类的概念类的定义实例例子分析类的成员数据成员属性成员方法成员静态成员博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 类的概念 在日常生活中&#xff0c;类是对具有相同特性的一类是物的抽象。比如水果是一个类&#xff0c;它是对…

Chapter 1 First Sight——30

The girl sitting there giggled. Id noticed that his eyes were black — coal black. 那个坐在那里的女孩笑着。我注意到她的眼睛是很色的--炭黑色的。 Mr. Banner signed my slip and handed me a book with no nonsense about introductions. Banner 先生签了我的名字然后…

GPU 与CPU的作用协调,工作流程、GPU整合到CPU得好处

在不少人的心目中&#xff0c;显卡最大的用途可能就只有两点——玩游戏、看电影&#xff0c;除此之外&#xff0c;GPU并没有其他的作用了。但是随着微软IE9的正式发布&#xff0c;不少人突然发现&#xff0c;微软一直提到一个名词&#xff1a;GPU硬件加速&#xff0c;从而也让不…

[luoguP1029] 最大公约数和最小公倍数问题(数论)

传送门 一.暴力枚举&#xff08;加了点优化&#xff09; #include <cstdio>int x, y, ans;inline int gcd(int x, int y) {return !y ? x : gcd(y, x % y); }inline int lcm(int x, int y) {return x / gcd(x, y) * y; }int main() {int i, j;scanf("%d %d", …

CPU和GPU擅长和不擅长的方面

从它们执行运算的速度与效率的方面来探讨这个论题。CPU和GPU都是具有运算能力的芯片&#xff0c; CPU更像“通才”——指令运算(执行)为重数值运算&#xff0c; GPU更像“专才”——图形类数值计算为核心。在不同类型的运算方面的速度也就决定了它们的能力——“擅长和不擅长”…

一些IO流的知识

IO流&#xff1a; 输入流&#xff1a;输出流&#xff1a; 字节流&#xff1a;字符流&#xff1a;为了处理文字数据方便而出现的对象。 其实这些对象的内部使用的还是字节流(因为文字最终也是字节数据) 只不过&#xff0c;通过字节流读取了相对应的字节数&#xff0c;没有对这些…

为人示弱,做事留余 | 摸鱼系列

我很喜欢结交有很好的自然观察能力的朋友&#xff0c;这是种对周围环境和文化的洞察力。 一方面的原因是优秀的领导者、企业家和投资人能利用这种能力发现新市场&#xff0c;预测新潮流&#xff0c;设计出有效的市场营销活动&#xff0c;并找到需要重点关注的人群。 另一方面&a…