JavaScript数组去重的6个方法


Posted in Javascript onJanuary 21, 2017

方法一

无需思考,我们可以得到 O(n^2) 复杂度的解法。定义一个变量数组 res 保存结果,遍历需要去重的数组,如果该元素已经存在在 res 中了,则说明是重复的元素,如果没有,则放入 res 中。

function unique(a) {
 var res = [];
 for (var i = 0, len = a.length; i < len; i++) {
 var item = a[i];
 for (var j = 0, jLen = res.length; j < jLen; j++) {
 if (res[j] === item)
 break;
 }
 if (j === jLen)
 res.push(item);
 }
 return res;
}
var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); // => [1, "1", "2"]

代码非常简单,那么是否能更简洁些?如果不考虑浏览器兼容,我们可以用 ES5 提供的 Array.prototype.indexOf 方法来简化代码。

function unique(a) {
 var res = [];
 for (var i = 0, len = a.length; i < len; i++) {
 var item = a[i];
 (res.indexOf(item) === -1) && res.push(item);
 }
 return res;
}
var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); // => [1, "1", "2"]

既然用了 indexOf,那么不妨再加上 filter。

function unique(a) {
 var res = a.filter(function(item, index, array) {
 return array.indexOf(item) === index;
 });
 return res;
}
var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); // => [1, "1", "2"]

方法二

法一是将原数组中的元素和结果数组中的元素一一比较,我们可以换个思路,将原数组中重复元素的最后一个元素放入结果数组中。

function unique(a) {
 var res = a.filter(function(item, index, array) {
 return array.indexOf(item) === index;
 });
 return res;
}
var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); // => [1, "1", "2"]

虽然复杂度还是 O(n^2),但是可以看到结果不同,1 出现在了数组最后面,因为结果数组取的是元素最后一次出现的位置。

方法三(sort)

如果笔试面试时只答出了上面这样 O(n^2) 的方案,可能还不能使面试官满意,下面就来说几种进阶方案。

将数组用 sort 排序后,理论上相同的元素会被放在相邻的位置,那么比较前后位置的元素就可以了。

function unique(a) {
 return a.concat().sort().filter(function(item, pos, ary) {
 return !pos || item != ary[pos - 1];
 });
}
var a = [1, 1, 3, 2, 1, 2, 4];
var ans = unique(a);
console.log(ans); // => [1, 2, 3, 4]

但是问题又来了,1 和 "1" 会被排在一起,不同的 Object 会被排在一起,因为它们 toString() 的结果相同,所以会出现这样的错误:

function unique(a) {
 return a.concat().sort().filter(function(item, pos, ary) {
 return !pos || item != ary[pos - 1];
 });
}
var a = [1, 1, 3, 2, 1, 2, 4, '1'];
var ans = unique(a);
console.log(ans); // => [1, 2, 3, 4]

当然你完全可以针对数组中可能出现的不同类型,来写这个比较函数。不过这似乎有点麻烦。

方法四 (object)

用 JavaScript 中的 Object 对象来当做哈希表,这也是几年前笔试时的解法,跟 sort 一样,可以去重完全由 Number 基本类型组成的数组。

function unique(a) {
 var seen = {};
 return a.filter(function(item) {
 return seen.hasOwnProperty(item) ? false : (seen[item] = true);
 });
}
var a = [1, 1, 3, 2, 1, 2, 4];
var ans = unique(a);
console.log(ans); // => [1, 3, 2, 4]

还是和方法三一样的问题,因为 Object 的 key 值都是 String 类型,所以对于 1 和 "1" 无法分别,我们可以稍微改进下,将类型也存入 key 中。

function unique(a) {
 var ret = [];
 var hash = {};
 for (var i = 0, len = a.length; i < len; i++) {
 var item = a[i];
 var key = typeof(item) + item;
 if (hash[key] !== 1) {
 ret.push(item);
 hash[key] = 1;
 }
 }
 return ret;
}
var a = [1, 1, 3, 2, '4', 1, 2, 4, '1'];
var ans = unique(a);
console.log(ans); // => [1, 3, 2, "4", 4, "1"]

虽然解决了讨厌的 1 和 "1" 的问题,但是还有别的问题!

function unique(a) {
 var ret = [];
 var hash = {};
 for (var i = 0, len = a.length; i < len; i++) {
 var item = a[i];
 var key = typeof(item) + item;
 if (hash[key] !== 1) {
 ret.push(item);
 hash[key] = 1;
 }
 }
 return ret;
}
var a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];
var ans = unique(a);
console.log(ans); // => [Object, String]

但是如果数组元素全部是基础类型的 Number 值,键值对法应该是最高效的!

方法五 (ES6)

ES6 部署了 Set 以及 Array.from 方法,太强大了!如果浏览器支持,完全可以这样:

function unique(a) {
 return Array.from(new Set(a));
}
var a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];
var ans = unique(a);
console.log(ans); // => [Object, Object, String, Number]

_.unique

最后来看看 underscore 对此的实现方式,underscore 将此封装到了 _.unique 方法中,调用方式为 _.unique(array, [isSorted], [iteratee])。其中第一个参数是必须的,是需要去重的数组,第二个参数可选,如果数组有序,则可以传入布尔值 true,第三个参数可选,如果需要对数组迭代的结果去重,则可以传入一个迭代函数。而数组元素去重是基于 === 运算符的。

其实很简单,underscore 中的实现方式和上面的方法一相似。

我们来看它的核心代码:

for (var i = 0, length = getLength(array); i < length; i++) {
 var value = array[i],
 // 如果指定了迭代函数
 // 则对数组每一个元素进行迭代
 computed = iteratee ? iteratee(value, i, array) : value;
 // 如果是有序数组,则当前元素只需跟上一个元素对比即可
 // 用 seen 变量保存上一个元素
 if (isSorted) {
 // 如果 i === 0,则直接 push
 // 否则比较当前元素是否和前一个元素相等
 if (!i || seen !== computed) result.push(value);
 // seen 保存当前元素,供下一次对比
 seen = computed;
 } else if (iteratee) {
 // 如果 seen[] 中没有 computed 这个元素值
 if (!_.contains(seen, computed)) {
 seen.push(computed);
 result.push(value);
 }
 } else if (!_.contains(result, value)) { 
 // 如果不用经过迭代函数计算,也就不用 seen[] 变量了
 result.push(value);
 }
}

外面的循环遍历数组元素,对于每个元素,如果数组有序,则和前一个元素比较,如果相同,则已经出现过,不加入到结果数组中,否则则加入。而如果有迭代函数,则计算传入迭代函数后的值,对值去重,调用 .contains 方法,而该方法的核心就是调用.indexOf 方法,和我们上面说的方法一异曲同工。

 以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
JavaScript 常用函数库详解
Oct 21 Javascript
一个报数游戏js版(约瑟夫环问题)
Aug 05 Javascript
jQuery判断iframe中元素是否存在的方法
May 11 Javascript
JavaScript函数模式详解
Nov 07 Javascript
Vue组件BootPage实现简单的分页功能
Sep 12 Javascript
jquery.multiselect多选下拉框实现代码
Nov 11 Javascript
Angular客户端请求Rest服务跨域问题的解决方法
Sep 19 Javascript
Vue利用路由钩子token过期后跳转到登录页的实例
Oct 26 Javascript
Angular2.0/4.0 使用Echarts图表的示例代码
Dec 07 Javascript
vue中手机号,邮箱正则验证以及60s发送验证码的实例
Mar 16 Javascript
详解给Vue2路由导航钩子和axios拦截器做个封装
Apr 10 Javascript
vue.js 中使用(...)运算符报错的解决方法
Aug 09 Javascript
微信小程序 scroll-view实现上拉加载与下拉刷新的实例
Jan 21 #Javascript
JS常用知识点整理
Jan 21 #Javascript
使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一)
Jan 21 #Javascript
原生js实现可拖动的登录框效果
Jan 21 #Javascript
微信小程序 WebSocket详解及应用
Jan 21 #Javascript
使用vue.js2.0 + ElementUI开发后台管理系统详细教程(二)
Jan 21 #Javascript
遍历json获得数据的几种方法小结
Jan 21 #Javascript
You might like
PHP5在Apache下的两种模式的安装
2006/09/05 PHP
资料注册后发信小技巧
2006/10/09 PHP
phpExcel中文帮助手册之常用功能指南
2014/08/18 PHP
js实现的真正的iframe高度自适应(兼容IE,FF,Opera)
2010/03/07 Javascript
jQuery Selector选择器小结
2010/05/06 Javascript
javascript使用正则控制input输入框允许输入的值方法大全
2014/06/19 Javascript
javascript数据类型示例分享
2015/01/19 Javascript
JS获取年月日时分秒的方法分析
2016/11/28 Javascript
Angularjs 动态添加指令并绑定事件的方法
2017/04/13 Javascript
Angular2下使用pdf插件的方法详解
2017/04/29 Javascript
vue router的基本使用和配置教程
2018/11/05 Javascript
详细讲解如何创建, 发布自己的 Vue UI 组件库
2019/05/29 Javascript
微信接入之获取用户头像的方法步骤
2019/09/23 Javascript
详解关闭令人抓狂的ESlint 语法检测配置方法
2019/10/28 Javascript
vue 使用post/get 下载导出文件操作
2020/08/07 Javascript
javascript解析json格式的数据方法详解
2020/08/07 Javascript
解决Vue keep-alive 调用 $destory() 页面不再被缓存的情况
2020/10/30 Javascript
[01:19:46]DOTA2-DPC中国联赛 正赛 SAG vs DLG BO3 第一场 2月28日
2021/03/11 DOTA
Python 自动补全(vim)
2014/11/30 Python
对于Python的Django框架部署的一些建议
2015/04/09 Python
Python实现打砖块小游戏代码实例
2019/05/18 Python
python根据时间获取周数代码实例
2019/09/30 Python
Python3标准库之threading进程中管理并发操作方法
2020/03/30 Python
Python标准库:内置函数max(iterable, *[, key, default])说明
2020/04/25 Python
Python 下载Bing壁纸的示例
2020/09/29 Python
Python Http请求json解析库用法解析
2020/11/28 Python
美国豪华时尚女性精品店:Kirna Zabête
2018/01/11 全球购物
面包店的创业计划书范文
2014/01/16 职场文书
毕业生自荐书
2014/02/02 职场文书
五年级科学教学反思
2014/02/05 职场文书
2014年四风问题自我剖析材料
2014/09/15 职场文书
大学毕业生个人总结
2015/02/28 职场文书
文明旅游倡议书
2015/04/28 职场文书
公司员工奖惩制度
2015/08/04 职场文书
2016年小学优秀班主任事迹材料
2016/02/29 职场文书
PostgreSQL 插入INSERT、删除DELETE、更新UPDATE、事务transaction
2022/04/12 PostgreSQL