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 相关文章推荐
用js查找法实现当前栏目的高亮显示的代码
Nov 24 Javascript
关于JavaScript的with 语句的使用方法
May 09 Javascript
jquery星级插件、支持页面中多次使用
Mar 25 Javascript
js的.innerHTML = &quot;&quot;IE9下显示有错误的解决方法
Sep 16 Javascript
一个简单的jquery进度条示例
Apr 28 Javascript
Javascript实现获取窗口的大小和位置代码分享
Dec 04 Javascript
Vue.js使用v-show和v-if的注意事项
Dec 13 Javascript
详解VUE中v-bind的基本用法
Jul 13 Javascript
js实现数组和对象的深浅拷贝
Sep 30 Javascript
Vue开发之watch监听数组、对象、变量操作分析
Apr 25 Javascript
详解VUE项目中安装和使用vant组件
Apr 28 Javascript
jQuery实现查看图片功能
Dec 01 jQuery
微信小程序 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
PHPExcel中文帮助手册|PHPExcel使用方法(分享)
2017/06/09 PHP
JQuery 构建客户/服务分离的链接模型中Table中的排序分析
2010/01/22 Javascript
jQuery 打造动态渐变按钮 详细图文教程
2010/04/25 Javascript
循环 vs 递归浅谈
2013/02/28 Javascript
js特殊字符转义介绍
2013/11/05 Javascript
jquery、js操作checkbox全选反选
2014/03/12 Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
2015/03/03 Javascript
JavaScript中用getDate()方法返回指定日期的教程
2015/06/09 Javascript
jQuery实现鼠标经过事件的延时处理效果
2020/08/20 Javascript
javaScript如何跳出多重循环break、continue
2016/09/01 Javascript
用js实现每隔一秒刷新时间的实例(含年月日时分秒)
2017/10/25 Javascript
React Native悬浮按钮组件的示例代码
2018/04/05 Javascript
关于Mac下安装nodejs、npm和cnpm的教程
2018/04/11 NodeJs
iview实现select tree树形下拉框的示例代码
2018/12/21 Javascript
js console.log打印对象时属性缺失的解决方法
2019/05/23 Javascript
解决jquery validate 验证不通过后验证正确的信息仍残留在label上的方法
2019/08/27 jQuery
优雅的使用javascript递归画一棵结构树示例代码
2019/09/22 Javascript
js实现课堂随机点名系统
2019/11/21 Javascript
[01:25:09]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第二场
2014/05/24 DOTA
基于Python对象引用、可变性和垃圾回收详解
2017/08/21 Python
Python学习pygal绘制线图代码分享
2017/12/09 Python
Tensorflow之Saver的用法详解
2018/04/23 Python
Python反射和内置方法重写操作详解
2018/08/27 Python
python实现一个简单的udp通信的示例代码
2019/02/01 Python
浅谈Python type的使用
2019/11/19 Python
Python读取实时数据流示例
2019/12/02 Python
解决Tensorflow 内存泄露问题
2020/02/05 Python
巴西男士个人护理产品商店:SHOP4MEN
2017/08/07 全球购物
先进集体获奖感言
2014/02/13 职场文书
亲子拓展活动方案
2014/02/20 职场文书
博士毕业生自我鉴定范文
2014/04/13 职场文书
小组口号大全
2014/06/09 职场文书
机械设计制造及其自动化专业求职信
2014/06/17 职场文书
水利局群众路线专题民主生活会发言材料
2014/09/21 职场文书
个人批评与自我批评发言稿
2014/09/28 职场文书
python实现网络五子棋
2021/04/11 Python