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 相关文章推荐
jquery加载页面的方法(页面加载完成就执行)
Jun 21 Javascript
showModalDialog在谷歌浏览器下会返回Null的解决方法
Nov 27 Javascript
js打开新窗口方法整理
Feb 17 Javascript
JavaScript中split与join函数的进阶使用技巧
May 03 Javascript
node.js cookie-parser 中间件介绍
Jun 06 Javascript
jquery popupDialog 使用 加载jsp页面的方法
Oct 25 Javascript
AngularJS中指令的四种基本形式实例分析
Nov 22 Javascript
js实现百度搜索提示框
Feb 05 Javascript
如何在vue里面优雅的解决跨域(路由冲突问题)
Jan 20 Javascript
使用layui的layer组件做弹出层的例子
Sep 27 Javascript
React Native中ScrollView组件轮播图与ListView渲染列表组件用法实例分析
Jan 06 Javascript
typescript配置alias的详细步骤
Aug 12 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
PHP学习之数组值的操作
2011/04/17 PHP
Symfony2框架学习笔记之表单用法详解
2016/03/18 PHP
js获取单选按钮的数据
2006/11/27 Javascript
js传参数受特殊字符影响错误的解决方法
2013/10/21 Javascript
jquery使用正则表达式验证email地址的方法
2015/01/22 Javascript
JavaScript数组前面插入元素的方法
2015/04/06 Javascript
日常收藏的jquery技巧
2015/12/02 Javascript
理解Javascript的call、apply
2015/12/16 Javascript
js精准的倒计时函数分享
2016/06/29 Javascript
原生js实现无限循环轮播图效果
2017/01/20 Javascript
Angular2下使用pdf插件的方法详解
2017/04/29 Javascript
微信小程序实现顶部下拉菜单栏
2018/11/04 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
2019/02/15 jQuery
webpack4 从零学习常用配置(小结)
2019/05/28 Javascript
Map与WeakMap类型在JavaScript中的使用详解
2020/11/18 Javascript
使用python分析统计自己微信朋友的信息
2019/07/19 Python
python网络爬虫 CrawlSpider使用详解
2019/09/27 Python
Python 生成器,迭代,yield关键字,send()传参给yield语句操作示例
2019/10/12 Python
python中的itertools的使用详解
2020/01/13 Python
探秘TensorFlow 和 NumPy 的 Broadcasting 机制
2020/03/13 Python
pycharm进入时每次都是insert模式的解决方式
2021/02/05 Python
加拿大在线旅游公司:Flighthub
2019/03/11 全球购物
Herschel美国官网:背包、手提袋及配件
2020/03/10 全球购物
财务人员个人求职信范文
2013/12/04 职场文书
表彰先进集体通报
2014/01/12 职场文书
大学生最新职业生涯规划书范文
2014/01/12 职场文书
最新个人职业生涯规划书
2014/01/22 职场文书
《理想的风筝》教学反思
2014/04/11 职场文书
质量在我心中演讲稿
2014/09/02 职场文书
高校自主招生校长推荐信
2015/03/23 职场文书
团组织推荐意见
2015/06/05 职场文书
2015年暑期社会实践方案
2015/07/14 职场文书
创业计划书之家教中心
2019/09/25 职场文书
十大最强飞行系宝可梦,BUG燕上榜,第二是飞行系王者
2022/03/18 日漫
nginx配置限速限流基于内置模块
2022/05/02 Servers
Golang实现可重入锁的示例代码
2022/05/25 Golang