第一篇 JS语言基础之数组去重

154 阅读3分钟

方案1 . 创建一个空数组 , 循环验证 + 数组方法

思路:
循环原有数组中的每一项 , 每拿到一项都往新数组中添加 .
添加之前验证新数组中是否存在这一项 ,不存在再增加 .

    let ary = [1,2,3,1,2,1,2,3,2,1,2,3]
    let newAry = [];
    for (let i = 0; i < ary.length; i++) {
        // 循环获取原有数组中的每一项
        let item = ary[i];
        // 验证新数组中是否存在这一项
        if(newAry.includes(item)){
            // 存在这一项,不再增加到新数组中,继续下一轮循环即可
            continue;
        }
        // 新数组中不存在这一项, 我们将其加入到新数组即可
        newAry.push(item);
    }
    console.log(newAry); // => [1,2,3]

优化 : 使用函数式编程 forEach

缺点 : 不兼容 IE6-8

    let ary = [1,2,3,1,2,1,2,3,2,1,2,3]
    let newAry = [];
    ary.forEach(item =>{
        if(newAry.includes(item)) return;
        newAry.push(item);
    });
    console.log(newAry); // => [1,2,3]

方案2 . 不创建空数组 , 循环比较 + 数组方法 双for循环 数组塌陷

思路
先分别拿出数组中的每一项A,用这一项和 "它后面的每项"依次进行比较 如果遇到和当前项A相同的,则在原来数组中把这一项移除掉
循环重复上述过程

不用 let , includes , indexOf , forEach 保证兼容性

    var ary = [1,2,3,1,2,1,2,3,2,1,2,3]
    for (var i = 0; i < ary.length; i++) {
        // item : 每一次循环拿出来的当前项
        // i : 当前项索引  i + 1 : 代表后一项
        var item = ary[i];
        // 让当前项和后面的每一项进行比较 (循环)
        for(var j = i + 1; j < ary.length; j++){
            // compare : 分别从 item 后面拿出来要比较的每一项
            var compare = ary[j]
            // 如果 compare 和 item 相等, 说明这一项是重复的, 我们把compare删掉
            if(compare === item){
                // j 索引这一项要从数组中移除
                ary.splice(j,1)
            }
        }
    }
    console.log(ary); // => [1,2,3,2]

上述结果并不是我们想要的, 仍旧有重复项未剔除

数组塌陷问题

    var ary = [10,20,30];
    for (var i = 0; i < ary.length; i++) {
        // 把当前项从数组中移除 => 原来数组会变
        ary.splice(i,1)
    }

image.png

解决: 数组塌陷后,为了不让i提前一位后跳加,我们先把i-- ,然后再继续

    var ary = [1,2,3,1,2,1,2,3,2,1,2,3]
    for (var i = 0; i < ary.length; i++) {
        // item : 每一次循环拿出来的当前项
        // i : 当前项索引  i + 1 : 代表后一项
        var item = ary[i];
        // 让当前项和后面的每一项进行比较 (循环)
        for(var j = i + 1; j < ary.length; j++){
            // compare : 分别从 item 后面拿出来要比较的每一项
            var compare = ary[j]
            // 如果 compare 和 item 相等, 说明这一项是重复的, 我们把compare删掉
            if(compare === item){
                // j 索引这一项要从数组中移除
                ary.splice(j,1)
                // 数组塌陷了: j后面的每一项索引都提前了一位, 下次要比较的应该还是j这个索引的内容,不能跳过.
                j--;
            }
        }
    }
    console.log(ary); // => [1,2,3]

过程注释版:

    var ary = [1,2,3,1,2,1,2,3,2,1,2,3]
    for (var i = 0; i < ary.length; i++) {
        // 第一轮
        // i = 0 item = 1 拿到第一项
        // 第二轮
        // i = 1 item = 2 ...
        var item = ary[i];
        for(var j = i + 1; j < ary.length; j++){
            // j =1 compore =2  2!==1
            // j =2 compore =3  3!==1
            // j =3 compore =1  1===1  ary.splice(3,1) [1,2,3,(1)2,...]
            // j =42就漏掉了 没有进行比较 所以 j-- j++
            // j =3 compore =2  ...
            var compare = ary[j];
            if(compare === item){
                ary.splice(j,1);
                j--;
            }
        }
    }
    console.log(ary); // => [1,2,3]

方案3 . 创建空对象 , 循环比较 + 数组方法

思路
创建一个空对象
循环数组中的每一项,把拿到的项当做对象的属性名和属性值存储起来
存储之前需要判断 如果 存储的项 和新对象里 同属性名的 属性值 相同,则重复
没有这一项,则新对象里同属性名的属性值是undefined,不相等,这一项不存在,放进去
对于重复项,用splice删除 并解决数组塌陷问题

image.png

    var ary = [1,2,3,1,2,1,2,3,2,1,2,3];
    //1.创建一个空对象
    let obj = {};
    // 2. 循环数组中的每一项,把每一项向对象中进行存储 => item : item
    for(let i = 0;i < ary.length;i++){
        let item = ary[i];
        // 3. 每一次存储之前进行判断 : 验证obj中是否存在这一项
        if(obj[item] !== undefined){
            // 说明此项已经存在 ,删除该项并解决数组塌陷问题 图1-1 然后继续循环
            ary.splice(i,1);
            i--;
            continue;
        }
            // 想把当前item变量存储的值给obj 所以不能用obj.item <=> obj['item']
        obj[item] = item; 
    }
    console.log(ary); //=> [1,2,3]

图 1-1

image.png

性能: 方案1的includes每次都要检索全部数组,性能较差 ; 方案2 双重循环,性能也不如方案3 .

但是 , 基于 splice 实现删除性能不好 : 当前项被删除后 , 后面每一项的索引都要向前提一位 ,如果后面内容过多 , 一定影响性能 .

性能优化思路: 把最后一项的值替换当前要删除的项,然后删除最后一项(已经被提前,不需要了重复了).同时,被替换的这个位置未参与过比较,所以还得需要参与一次比较才完整.

性能优化图解:

image.png

性能优化后的代码

    var ary = [1,2,3,1,2,1,2,3,2,1,2,3];
    let obj = {};
    for(let i = 0;i < ary.length;i++){
        let item = ary[i];
        if(obj[item] !== undefined){
            // 替换
           ary[i]=ary[ary.length-1];
           // 删除最后一项
           ary.length--;
           // 不让i累加保证比较的完整性
            i--;
            continue;
        }
        obj[item] = item; 
    }
    console.log(ary); //=> [1,2,3]

封装成函数

/* 
 *  unique : 实现数组去重的方法
 *  
 *  @params
 *      ary [Array] 要去重的数组
 *  @return
 *      [Array] 去重后的数组
 *  by Ark on 20190724
 * 
 */
    function unique(ary){
        let obj = {};
        for(let i = 0;i < ary.length;i++){
            let item = ary[i];
            if(obj[item] !== undefined){
                ary[i]=ary[ary.length-1];
                ary.length--;
                i--;
                continue;
            }
            obj[item] = item; 
        }
        return ary;
    }

方案4 . 正则

目前看看就好

    let ary = [12,23,12,15,25,23,25,14,16];
    ary.sort((a,b)=> a - b );
    let str = ary.join('@') + '@';
    console.log(str); //=> 12@12@14@15@16@23@23@25@25@
    let reg =/(\d+@)\1*/g // \1* 即表示与前一项一模一样的项又出现0到多次
    console.log(str.match(reg)); //=> (6) ['12@12@', '14@', '15@', '16@', '23@23@', '25@25@']
    let newAry = [];
    str.replace(reg, (n,m) =>{
        // 捕获适配的每一项
        console.log('n:'+n);
        console.log('m:'+m);
        m = Number(m.slice(0,m.length-1));
        newAry.push(m);
    });
    console.log(newAry); //=> (6) [12, 14, 15, 16, 23, 25]

image.png

基于ES6的 Set( 对应的 Map ) 实现去重

    let ary = [12,23,12,15,25,23,25,14,16];
    ary = [...new Set(ary)];
    console.log(ary); //=> (6) [12, 14, 15, 16, 23, 25]