uniq函数,是Underscore中的一个数组去重函数,给它传递一个数组,它将会返回该数组的去重副本。javascript
1 ES6版本去重
在ES6版本中,引入了一个新的数据结构——set,这是一种相似数组的数据结构,它有个最大的特色就是内部的每个元素都是独一无二的,因此咱们能够利用它来对数组进行去重:java
var uniq = function(array) { var set = new Set(array); return [...set]; }
这是目前而言最快速简介的数组去重方法。可是因为浏览器兼容问题,目前ES6尚未彻底普及,这样的方法可能在老旧版本的浏览器当中没法起到做用。因此咱们仍是须要使用ES5来实现。git
2 ES5版本去重
对于接受的数组,咱们能够对其进行遍历,使用一个result数组存放独一无二的元素,对于传入数组的每一项,在result中进行检索,若是result中不存在,那么就推入result中,最后返回result便可:es6
var uniq = function(array) { var result = []; var length = array.length; var i; for(i = 0; i < length; i++) { if(result.indexOf(array[i]) < 0) { result.push(array[i]); } } return result; };
该函数已经可以比较简单的数值、字符串、布尔值等简单值了,可是若是是复杂对象的话,可能就达不到去重的目的,好比:github
var objArr = [{name: 'a'}, {name: 'a'}]; console.log(uniq(objArr));
咱们可能会但愿返回值是[{name: 'a'}],可是因为连个对象引用值不相等,因此比较时,不会对这两个对象进行去重,致使最后返回的结果是两个都存在,这显然不是咱们所指望的。数组
咱们须要一个指定比较规则的函数。浏览器
3 规则定制版去重函数
咱们没法预知用户传递的数组内元素的类型,因此咱们最好可以让用户自定义比较规则,最好的办法就是让用户传递函数做为参数。数据结构
默认函数接受的参数即为数组中的某一项:函数
var uniq = function(array, func) { var result = []; var length = array.length; var i; if(!func) { for(i = 0; i < length; i++) { if(result.indexOf(array[i]) < 0) { result.push(array[i]); } } } else { var seen = []; for(i = 0; i < length; i++) { if(seen.indexOf(func(array[i])) < 0) { seen.push(func(array[i])); result.push(array[i]); } } } return result; };
在func没有被传递时,直接进行比较;若是传递了func函数,那么对于array中的每一项,使用func处理后的返回值再进行比较,这样就能够达到对象比较的目的。url
再次使用对象进行实验:
var objArr = [{id: 'a'}, {id: 'a'}, {id: 'b'}]; console.log(uniq(objArr, function(item) { return item.id; }));
输出结果中只有两个对象,说明达到了要求。
传递了这个自定义函数以后,去重的灵活性就大大的增长了。好比对于一个传递的对象数组,其中的每一个对象都包含两个属性——name和age,咱们须要比较这些对象,只有当name和age都相同的时候,咱们才认为两个对象相同,那么:
var persons = [{name: 'dm', age: 22}, {name: 'dm', age: 23}, {name: 'dm', age: 22}]; console.log(uniq(persons, function(item) { return item.name + item.age; }));
最后返回的结果可以符合咱们去重的要求。
如今去重的问题解决了,能够提升一下效率吗?
若是咱们获得的是一个有序的数组(不管是数组排序仍是字符串排序),咱们能够只比较相邻两项是否相同来去重,这样更加简单快速。
4 快速去重
咱们能够给uniq函数新增一个参数——isSorted,表明传递的数组是不是有序数组。
var uniq = function(array, isSorted, func) { var result = []; var length = array.length; var i; var seen = []; if(isSorted && !func) { for(i = 0; i< length; i++) { if(array[i] == seen) continue; else { result.push(array[i]); seen = array[i]; } } } else if(func){ for(i = 0; i < length; i++) { if(seen.indexOf(func(array[i])) < 0) { seen.push(func(array[i])); result.push(array[i]); } } } else{ for(i = 0; i < length; i++) { if(result.indexOf(array[i]) < 0) { result.push(array[i]); } } } return result; };
这样的实现就比较完善了,其中重要的点是对于seen这个变量的运用。
以上代码的实现思想就是来源于Underscore,只不过实现得比Underscore更加简陋,相对而言不那么完善。
5 Underscore实现数组去重
如下就是Underscore的源码(附注释):
// Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // The faster algorithm will not work with an iteratee if the iteratee // is not a one-to-one function, so providing an iteratee will disable // the faster algorithm. // Aliased as `unique`. //数组去重函数,使得数组中的每一项都是独一无二的。 _.uniq = _.unique = function (array, isSorted, iteratee, context) { //若是没有传递isSorted参数(即传递值不是Boolean类型),那么默认为false,其他参数从新赋值。 if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } //若是传递了iteratee,那么使用cb方法包装(确保返回一个函数),而后从新赋值。 if (iteratee != null) iteratee = cb(iteratee, context); //保存结果。 var result = []; //用于存放array的值便于下一次比较,或者用于存储computed值。 var seen = []; //遍历array数组。 for (var i = 0, length = getLength(array); i < length; i++) { //value表示当前项,computed表示要比较的项(有iteratee时是iteratee的返回值,无iteratee时是value自身)。 var value = array[i], computed = iteratee ? iteratee(value, i, array) : value; if (isSorted && !iteratee) { //若是数组是有序的,而且没有传递iteratee,则依次比较相邻的两项是否相等。 //!0===true,其他皆为false。 if (!i || seen !== computed) result.push(value); //seen存放当前的项,以便于下一次比较。 seen = computed; } else if (iteratee) { //若是传递了iteratee,那么seen就用于存放computed值,便于比较。 //之因此不直接使用result存放computed值是由于computed只用于比较,result存放的值必须是原来数组中的值。 if (!_.contains(seen, computed)) { seen.push(computed); result.push(value); } } else if (!_.contains(result, value)) { //isSorted为false而且iteratee为undefined。 //能够理解为参数数组中是乱序数字,直接比较就行了。 result.push(value); } } return result; };
数组去重是一件说容易也容易,说简单也简单的事情,就看你怎么作了。
更多Underscore源码解读:GitHub