JS数组去重多种方法

javascript.gif

1.怎么解决一个数组去重问题

我看了很多面试相关的问题,发现数组去重出现过几次,这里就对数组去重问题进行一个总结。并且进行扩展。请一定要看到第二章!

问题描述:数组去重,顾名思义就是,把数组里的重复数值去除,使其多个相同的数值变为一个,最后使数组里不含有重复数值。

举个例子:有个数组:[1,2,3,4,4,5,5,5,6,7],数组去重后就变为[1,2,3,4,5,6,7]

该问题有很多解决方法,这里将一一列出,并且会对其个别重要知识点进行扩展

解法将由浅入深😏,一定要看到最后几个解法!

🤔 解法1:

使用双重for和splice

// 双重for加splice
function unique(arr){            
    for(var i=0; i<arr.length; i++){
        for(var j=i+1; j<arr.length; j++){
            if(arr[i]==arr[j]){         
            //第一个等同于第二个,splice方法删除第二个
                arr.splice(j,1);
                j--;
            }
        }
    }
return arr;
}
复制代码

🤔 解法2

使用indexof方法和新数组

//使用indexof
function unique(arr) {
    var array = [];//用新数组来装
    for (let i = 0; i < arr.length; i++) {
        if (array.indexOf(arr[i]) === -1) {
            //indexof返回-1表示在新数组中不存在该元素
            array.push(arr[i])//是新数组里没有的元素就push入
        }
    }
    return array;
}
复制代码

使用includes也可以判断是否含有某值

function unique(arr) {
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes(arr[i]) ) {
            //includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
复制代码
  • indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。有两个参数,第一个参数是要查找的元素,第二个参数可选,是开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0

  • includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。其也有两个参数,第一个是要查找的元素,第二个可选,是开始查找的位置,与indexof相同的是,第二个参数为负值的话,就从末尾开始往前跳 参数 的绝对值个索引,然后往后搜寻。默认为 0

🤔 解法3

使用sort方法先排序,使相同的元素都相邻

function unique(arr) {
    arr = arr.sort((a, b) => a - b)//sort先按从小到大排序
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
复制代码

sort方法用于从小到大排序(返回一个新数组),其参数中不带以上回调函数就会在两位数及以上时出现排序错误(如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。两位数会变为长度为二的字符串来计算)。所以自己要写一个排序标准,当回调函数返回值大于0时两个值调换顺序。

🤔 解法4

ES6 提供了新的数据结构 Set。Set可以非常简单的去重

function unique(arr) {
    const result=new Set(arr);
    return [...result];
    //使用扩展运算符将Set数据结构转为数组
}
复制代码

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

🤔 解法5

使用Map

function unique(arr) {
    let map = new Map();
    let array = new Array();  // 数组用于返回结果
    for (let i = 0; i < arr.length; i++) {
      if(map.has(arr[i])) {  // 如果有该key值
        map.set(arr[i], true); 
      } else { 
        map.set(arr[i], false);   // 如果没有该key值
        array.push(arr[i]);
      }
    } 
    return array ;
}
复制代码

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

  • Map.prototype.has(key) 返回一个布尔值,表示Map实例是否包含键对应的值。
  • Map.prototype.set(key, value) 设置Map对象中键的值。返回该Map对象。

🤔 解法6

使用filter

function unique(arr) {
    return arr.filter(function (item, index, arr) {
        //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
        //不是那么就证明是重复项,就舍弃
        return arr.indexOf(item) === index;
    })
}
复制代码

filter英文意思是筛选,filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。其回调函数包含三个参数(数组中当前正在处理的元素,在处理的元素在数组中的索引(可选),调用了 filter 的数组本身(可选))

🤔 解法7

使用reduce加includes

function unique(arr){
    let result=arr.reduce((acc,cur)=>{
        if(!acc.includes(cur)){
            acc.push(cur);
        }
        return acc;
    },[])//[]作为回调函数的第一个参数的初始值
    return result
}
复制代码

2.数组去重解法7引出关于reduce的另一道面试题

reduce的用处很多,很重要,这里补上上面对reducer的知识点解释。

reduce介绍

MDN中对其的描述是:reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。其能做的功能很多,通过回调函数实现。

reduce的第一个参数是个回调函数,其有四个参数(累加器,当前值,当前索引,原数组),后两个参数可选。第二个参数是回调函数的第一个参数累加器的初始值(很重要)

注意:不给初始值,那么初始值就是原数组的第一个元素,计算从第二个元素开始。给了初始值就是从第一个元素开始

通过代码实现功能了解reduce与其回调函数的书写:

  • 为存储数值的数值进行累加求和
let result=[1,2,3,4].reduce((acc,cur)=>{
    return acc+cur;//acc为累加器(初始值为数组第一个元素),cur为当前元素
})
console.log(result);//输出10
//因为没初始值,所以从数组第二个元素开始计算,所以处理上面数组,回调函数共运行了3次
复制代码
  • 累加求和时给初始值
let result=[1,2,3,4].reduce((acc,cur,index,o)=>{
    return acc+cur;
},10)
console.log(result);//输出20
//因为有初始值,所以从数组第一个元素开始计算,所以处理上面数组,回调函数共运行了4次
复制代码
  • 按属性对object进行分类
const bills=[{type:'transfer',momey:233},
             {type:'study',momey:341},
             {type:'shop',momey:821},
             {type:'transfer',money:821},
             {type:'study',momey:821}
            ]
let result=bills.reduce((acc,cur)=>{
    if(!acc[cur.type]){//遇到不存在的类型,就新建一个空数组来装
        acc[cur.type]=[];//二维数组
    }
    acc[cur.type].push(cur)
    return acc;
},[])//为累加器设置初始值为空数组,作为分类用的容器
console.log(result);
//输出
[
  transfer: [{ type: 'transfer', momey: 233 },{ type: 'transfer', money: 821}],
  study: [ { type: 'study', momey: 341 }, { type: 'study', momey: 821 } ],
  shop: [ { type: 'shop', momey: 821 } ]
]
复制代码

reduce相关面试题

🤔 题目描述:请使用原生 JavaScript 实现一个方法,判断 html 中出现次数最多的标签,并统计这个次数。

知识点细化:

  • 获取所有标签:

    document.querySelector(*):列出页面内所有标签,*表示选择器*,也就是全部。

  • Object.entries():

    Object.entries()返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。简单的来说就是可以把对象的每个属性变为一个数组,这个数组里有两个值,一个为属性名,一个为属性值。例如:Object.entires({a:1,b:2,c:3})会得到[ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]

思路分析:
1. 先获得含有所有标签的NodeList数组,然后将其加工为只有标签名的数组,接着使用reduce得到一个对象,以标签名为属性名,标签数量为属性值的对象。
2.将上一步得到的对象用Object.entires()变为个二维数组,再使用reduce对其处理,得到数量最多的那个标签(比较每个数组的tags[1],返回数组的tags[0])。

答案代码:

 window.onload=function(){
    // 最大数的思路是JS 必考的 使用reduce
    const maxBy=function(list,tag){
        return list.reduce(
            function(x,y){
                //根据reduce方法获得数量最大的那个标签
                return tag(x)>tag(y)?x:y
            }
            )
    }
    function getFrequentTag(){
        //得到reduce 需要的数组
        const tags=[...document.querySelectorAll('*')].map(x=>x.tagName).reduce((acc,tag)=>{
            acc[tag]=acc[tag]?acc[tag]+1:1;
            //数组存在该元素,就值+1,否则创建元素,设置值为1
            return acc;//得到以tag名为属性名,数量为属性值的对象
        },{})//初始值为对象
        return maxBy(Object.entries(tags),tag=>tag[1])
        //tag=>tag[1]这个函数表示return数组的第二个值,也就是标签的数量
    }
    console.log(getFrequentTag());
}
复制代码

扩展:如何得到第二多的标签,以及数量第X大的标签呢?

对上面的解法进行进一步的优化,我们要做的不在是得到最大,而是要得到任意大的标签!
使用sort方法改进,对list数组进行排序,再到list数组中取相应位置的数值。

代码:

//获得第X多的标签

window.onload=function(){
    // 最大数的思路是JS 必考的 使用reduce
    const maxByx=function(list,tag,x){
        list=list.sort((a,b)=>{
            return tag(b)-tag(a);//数量从大到小排序
        })
        return list[x];
    }
    function getFrequentTag(){
        //得到reduce 需要的数组
        const tags=[...document.querySelectorAll('*')].map(x=>x.tagName).reduce((acc,tag)=>{
            acc[tag]=acc[tag]?acc[tag]+1:1;//数组存在该元素,就值+1,否则创建元素,设置值为1
            return acc;//得到以tag名为属性名,数量为属性值的对象
        },{})//初始值为对象
        return maxByx(Object.entries(tags),tag=>tag[1],1)
        //第三个参数用于指定要第几大的,这里指定第二大的
        //tag=>tag[1]这个函数表示return数组的第二个值,也就是标签的数量
    }
    console.log(getFrequentTag());
}
复制代码

根据上面代码return maxByx(Object.entries(tags),tag=>tag[1],1),我们只需要指定第三个参数值就能够得到我们要的数量排名第X的的那个标签。
这里就完成了面试中对得到html中最大数量标签的升华了!

感谢阅读,有好的建议请一定提出,

参考文章:

https://juejin.cn/post/6942435294549377054

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值