前言
有时候我们做项目的时候往往会需要把数组里面一些重复的项去掉, 但是原生 JS 有排序, 有筛选等等, 但是就是没有数组去重怎么办呢?
这能怎么办, 自己手动实现嘛.
数组不像对象和 hash 有唯一的标志特征 (key). 所以, 数组去重的核心就是[1] 数组内元素互相比较, 然后放入新的数组中.[2] 参照对象构建一个唯一的特征标志, 然后放入新数组中. 以下就是依照这种思路产生的方法.[3] 数组中含对象的去重方式我采用使用 JSON.stringify() 将对象转换成 JSON 字符串进行比较的方式.
1. 最基础的去重: 双重遍历
双重遍历的核心就是依据[1] , 通过拿出一个元素和剩下的元素依次比较, 如果全部不相等则证明此元素为唯一.
- let a=[{a:1},{b:2},{c:3},{a:1},{d:2}]
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let b=[]
- for(let i=0;i<arr.length;i++){
- let unexit=true
- for(let j=i+1;j<arr.length;j++){
- if(JSON.stringify(arr[i])===JSON.stringify(arr[j])){
- unexit=false
- break
- }
- else{
- unexit=true
- }
- }
- if(unexit){
- b.push(arr[i])
- }
- }
- return b
- }
关于数组中存在对象, 是采用 JSON.stringify()转换成 JSON 字符串进行的比较, 后续不再叙述. 双重遍历的缺点是复杂度太高.
上面的代码去重得到的结果的顺序会改变, 所以如果想要顺序按照原有顺序, 数组在进行去重时建议重新申明一个新的数组 (var new=old.reverse() ) 得到一个新的相反的数组, 最后再使用 reverse(). 之所以新建数组而不是直接取反是因为: reverse()会修改原数组.
2.Array.prototype.sort(): 相邻元素去重
相邻元素去重的核心在于 Array.sort()能够对数组进行排序. 这样相等的数组就会在相邻的位置, 通过比较相邻的元素就可以起到去重的作用[1] .
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let Arr=arr.sort()
- let b=[]
- for(let i=0;i<Arr.length;i++){
- if(Arr[i]!==Arr[i+1]){
- b.push(Arr[i])
- }
- }
- return b
- }
Array.prototype.sort()方法可以使用 array.sort((a,b)=>{a.key-b.ky})进行对象的排序, 前提是数组中的对象存在相同的 key 值.
3.Object.keys(): 存在唯一性
在一个对象里面 key 值是唯一的, 所以通过遍历数组给每个数组一个标志, 通过标志去重[2]
- let a=[{a:1},{b:2},{c:3},{a:1},{d:2}]
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let b=[]
- let hash={}
- for(let i=0;i<arr.length;i++){
- if(!hash[JSON.stringify(arr[i])]){
- hash[JSON.stringify(arr[i])]=true
- b.push(arr[i])
- }
- }
- return b
- }
4. 双重遍历去重改良之: indexOf
双重遍历的思路我们都知道, 先拿出一个元素, 然后使用循环再次遍历数组去一一比较. 如果有一个方式能够让我们不再遍历一遍数组, 那么复杂度相对而言会减少一点.
indexOf 方法返回给定元素在数组中第一次出现的位置, 如果没有出现则返回 - 1. 首先我们新建一个空数组(arry), 如果: arry.indexOf(数组元素)===-1, 那么我们就可以知道 arry 中不存在元素.
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let b=[]
- for(let i=0;i<arr.length;i++){
- if(b.indexOf(arr[i])==-1){
- b.push(arr[i])
- }
- }
- return b
- }
indexOf 方法可返回某个指定的字符串值在字符串中首次出现的位置. 所以对象不适用, 因为对象转为字符串就都会变成{object,object} , 无法比较.
5. 循环遍历之: map()/forEach()
map()和 forEach()都可以实现遍历数组. 所以以上的方法都可以用 map(),forEach()改写. 下面我只简单的改写一个, 其他的改写方式参照即可.
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let b=[]
- arr.forEach(res=>{
- if(b.indexOf(res)==-1){
- b.push(res)
- }
- })
- return b
- }
6.ES6:Set 数据结构
Set 数据类似于数组, 但是成员的值都是唯一的, 没有重复的值. 它可以接收一个数组, 类于: let a=[1,2,3,1,2] Set(a)=>1,2,3 所以可以使用 Set()实现去重.
- let c=[1,2,3,4,5,6,1,2,3]
- function unique(arr){
- let b=new Set(arr)
- let c=Array.from(b)
- return c
- }
Set 去重不适用于含对象的数组, 因为 Set 的去重参照的是(===), 数组中的元素对象, 虽然可能数值相等, 但是地址不相等. 所以 Set 无法实现去重.
7. 总结
实现数组的去重, 要么通过元素对比, 要么设置特殊标志识别. 元素对比的思路有 2 种: 一种是和原数组一一对比; 另一种和新的数组对比.
如果要实现含对象的数组去重, 一般使用遍历的方式, 包括使用遍历类的方法(map,forEach,reduce 等). 像 Set,sort 等通过改变数组的方式一般是不可行的.
来源: https://www.jb51.net/article/159191.htm