一起又双叒叕看数组去重
Posted 前端矿场常年招矿工啦啦啦 - muyuxingguang@
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起又双叒叕看数组去重相关的知识,希望对你有一定的参考价值。
正所谓“一山不容二虎,一渊不藏两蛟”,在某些“矫情”的需求中,数组中不能存在重复的元素,于是就有了对数组去重方法的讨论,关于数组去重的方法由来已久,我当然也想不出什么原创的方法了,这里只是简单的总结一下以备忘。
不过,我们要先声明一下,上面的这句强行组成的谚语还有下一句叫做“除非一公一母” ;也就是说如果是一公一母的两个元素是可以同时存在的,为了避免混淆,这里规定元素之间的比较为严格相等,两个元素通过 === 比较返回 true 的视为相同元素,需要去重。接下来我们统一一下函数风格。函数名为Deduplication
,接受参数为数组,返回的参数也为数组。在正式开始写代码之前,还需要明确一点就是数组去重不是找出数组中只出现一次的元素,而是让重复的元素有且仅出现一次。好了接下来一一列举我抄袭的数组去重方法,其实我还是修改了一些错误并且优化了一丁点的。
Idea1
双层循环,外层循环待去重数组,内层循环检查结果数组
如果在结果数组中有相同的值则跳过,不相同则push进结果数组
Solution1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function Deduplication(arr) { var result = []; for ( var i = 0, alen = arr.length; i < alen; i++) { var item = arr[i]; for ( var j = 0, rlen= result.length; j < rlen; j++) { if (result[j] ===item) break ; } if (j === rlen) //如果遍历完结果数组还没找到,说明不是重复的元素 result.push(item); } return result; } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(unique(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
当然如果不考虑兼容性的话,可以使用ES5新增加的数组迭代迭代方法和位置方法。
solution2:
1 2 3 4 5 6 7 8 9 10 11 12 | function Deduplication(arr) { var result = []; arr.forEach ( function (item,index,arr){ if (result.indexOf(item)===-1){ result.push(item); } }); return result; } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(unique(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
更巧妙的方法是使用fliter来过滤原数组
solution3:
1 2 3 4 5 6 7 8 | function Deduplication(a) { return a.filter( function (item, index, array) { return array.indexOf(item) === index; //indexOf方法只会返回元素第一次出现的位置,所以元素第一次出现时会是true,后面再出现就是false了 }); } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
Idea2
双层循环,外层循环待去重数组,内层循环检查外层循环当前项与其后面的所有的项
如果在当前项后面发现有相同的值,则跳过,否则push进结果数组
solution4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function Deduplication(arr) { var result = []; for ( var i = 0,len=arr.length; i <len; i++) { for ( var j = i + 1; j < len; j++){ if (arr[i] === arr[j]){ break ; //发现相同值就不需要循环了,而且后面的if判断语句也不会通过,相当于执行下次外层循环了 } } if (j===len) //未发现后面有相同值 result.push(arr[i]); } return result; } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, \'2\', 5, 8, 4, 6 ] |
观察返回的数组会发现与前面的方法返回的不一样,但是还是出掉了重复的元素,仔细品读源代码会发现在发现当前项后面有重复项时,当前项并没有放入结果数组,而是继续循环,这样操作的结果是只有某个元素最后一次出现的位置才会被push进结果数组,而不像前面的方法是在元素第一次出现的时候就push进了数组。稍稍修改一下源代码,下面的源代码也是基于这种思想的。
solution5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function Deduplication(arr){ var result = []; for ( var i = 0,len=arr.length; i < len; i++){ for ( var j = i + 1; j < len; j++){ if (arr[i] === arr[j]){ j=++i+1; //如果当前项后面找到了重复元素,i自增一次并返回+1的值给j,相当于进入了下一次外循环 } } result.push(arr[i]); } return result; } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, \'2\', 5, 8, 4, 6 ] |
前面的两种方法结果打乱了元素在原始数组中的顺序,如果支持ES5的话,则可以用下面的方法保持元素的原始顺序。
solution6:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function Deduplication(){ var result = []; arr.forEach( function (item, index ,arr){ //这里利用map,filter方法也可以实现 //从传入参数的下一个索引值开始寻找是否存在重复 if (arr.indexOf(item,index+1) === -1){ result.push(item); } }) return result; }; var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
Idea3
思路:
双层循环,外层循环元素,内层循环时比较值
值相同时,则删去这个值
solution7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function Deduplication(arr){ for ( var i = 0,len=arr.length; i < len; i++){ for (j = i + 1; j < len; j++){ if (arr[i] === arr[j]){ arr.splice(j,1); //删除后面的重复元素,但是要注意的是删除一个元素过后,原数组的长度会发生变化所以len要-1, //j也要减一,这是为了防止当前项后面出现两个相邻的重复元素的情况时,删掉前一个,后一个前移 //下次内循环时j++后就漏掉了后一个 len--; j--; } } } return arr; }; var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
也许你可以注意到这里是直接通过数组的splice()方法直接在原数组上删除元素的,arr是引用类型,这里也可以不需要返回值。
solution8:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function Deduplication(arr){ for ( var i = 0,len=arr.length; i < len; i++){ for (j = i + 1; j < len; j++){ if (arr[i] === arr[j]){ arr.splice(j,1); //删除后面的重复元素,但是要注意的是删除一个元素过后,原数组的长度会发生变化所以len要-1, //j也要减一,这是为了防止当前项后面出现两个相邻的重复元素的情况时,漏掉了后一个 len--; j--; } } } }; var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; Deduplication(arr); console.log(arr); //[ 2, 8, 6, \'2\', 5, 4 ] |
Idea4
思路:对象的不能有两个相同的属性,就像哈希表一样
准备一个空的结果数组和一个空对象,循环遍历数组
如果当前数组元素已经是对象的属性则跳过,否则,将此元素作为对象的键,值可以为任意有效值如true,并将此元素push进结果数组
solution9:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function Deduplication(arr){ var obj = {},result = []; for ( var i = 0,len=arr.length; i<len; i++){ if (!obj[arr[i]]){ //如果能查找到,证明数组元素重复了 obj[arr[i]] = true ; result.push(arr[i]); } } return result; }; var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, 5, 4 ] |
当然如果支持ES5的话,上面的思想还可以简化为:
solution10:
1 2 3 4 5 6 7 8 | function Deduplication(arr) { var obj = {}; return arr.filter( function (item) { return obj.hasOwnProperty(item) ? false : (obj[item] = true ); }); } var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, 5, 4 ] |
细心的小伙伴有没有发现一个问题,数组中的字符串\'2\'并没有出现在结果数组中,这是因为javascript中对象的属性都是字符串,如果不是会自动转化,这样以来数字2和字符串\'2\',对应的属性是相同的,字符串\'2\'就被当做重复的元素过滤掉了,所以这种方法比较适合于数组元素都是数字或者字符串的情况。不过我们仍然可以通过一些小技巧解决这个问题,可以把元素的数据类型也作为对象属性的一部分。
solution11:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function Deduplication(arr){ var obj = {},result = []; for ( var i = 0,len=arr.length; i<len; i++){ var item=arr[i]; var key= typeof (item)+item; if (!obj[key]){ //如果能查找到,证明数组元素重复了 obj[key] = true ; result.push(arr[i]); } } return result; }; var arr=[2,8,6, \'2\' ,5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, 8, 6, \'2\', 5, 4 ] |
Idea5
思路:排序后再删除重复元素(so这个方法会打乱数组元素原来的顺序)
排序后的重复元素会变成相邻元素
比较相邻元素,删除重复值
solution12:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function Deduplication(arr){ arr.sort(); //注意这个地方的concat:返回数组的副本并排序 for ( var i =0,len=arr.length; i <len; i++) { if (arr[i]===arr[i+1]) { arr.splice(i,1); len--; i--; } } return arr; }; var arr=[2,8,6,\'2\',5,6,4,5,8,4,6]; console.log(Deduplication(arr)); //[ 2, \'2\', 4, 5, 6, 8 ] |
如果可以使用ES5特性的话,可以使用一下几种变形
solution13:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function Deduplication(arr){ var newarr = arr.concat().sort(); //注意这个地方的concat:返回数组的副本并排序 newarr.sort( function (a,b){ //再对副本数组 |