简单纪录一下数组去重的方法,嘻嘻

130 阅读3分钟

最近在网上数组去重的方法很多,想了一下无非就是两种

一、两层循环法

let array = [0,1,2,'a','b']
let arr2 = [0,12,'a','abc','d']
console.log(typeof arr2);
/**
 * 这里打印数组的类型是object,因为这里创建一个数组的过程是简化的写法,为语法糖。就相当于let arr = new Array
 * 判断Array的的时候,我们可以使用 instanceof 来判断
 */
 console.log(arr2 instanceof Array);
 let newArray = array.concat(arr2);

循环遍历,利用for循环嵌套for循环,然后splice去重(Es5中最常用)
双层循环,外层循环元素,内层循环时比较值。

function remove (arr) {
    for(let i = 0;i<arr.length;i++){
        for(let j = i+1;j<arr.length;j++){
            if(arr[i]===arr[j]){
                arr.splice(j,1);
                j--;
            }
        }
    }    
    return arr;
}
remove(newArray);

二、利用语法自身键不可重复性

function remove2(arr){
    if(!Array.isArray(arr)){
        console.log('type error');
        return;
    }
    var array = [];
    for(let i=0;i<arr.length;i++){
        if(array.indexOf(arr[i]) === -1){
            array.push(arr[i])
        }
    }
    return array;
}
remove(newArray);

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组

三、利用set去重 (ES6中最常用)

let newArray = new Set(array.concat(arr2));

嘻嘻,用到的都是js中操作数组比较常用的方法



这是一条华丽的分割线

最近看面试题时遇到了几种不同for循环,因为没有深入了解导致做题时无法区分它们的用法,尤其是在以及在使用时的注意点。因此对js中的四种for循环进行了总结区分:

在ES5中,for循环有三种,分别是for/for-in/forEach
在ES6中,新增了一种循环,for-of

一、最简单的for循环(循环代码块一定的次数)

let array = [0,1,2];
for(let i = 0;i<array.length;i++){
    console.log(array[i]);
}
// 如果数组长度在循环过程中不会改变,将数组长度用变量存储起来会获得更好的效率
let arr = ['a','b','c'];
for(let i = 0, long = arr.length; i<long; i++){
    console.log(arr[i]);
}

二、for-in(循环遍历对象的属性)

for(let index in arr){
    console.log("arr["+index+"]=" + arr[index]);
}
// for-in 循环遍历的是对象的属性,而不是数组的索引。因此, for-in 遍历的对象不局限于数组,还可以遍历对象。
// 而且for-in 不仅仅遍历 array 自身的属性,其还遍历 array 原型链上的所有可枚举的属性
const person = {
    name: 'jack',
    like: 'basketball'
}
person.age = 18;
for(let i in person){
    console.log("person["+i+"]=" + person[i]);
}
// 需要注意的是,for-in遍历属性的顺序并不确定,即输出的结果顺序与属性在对象中的顺序无关,与任何顺序都无关。
// 其实for-in并不适合用来遍历Array中的元素,其更适合遍历对象中的属性,这也是其被创造出来的初衷。

三、forEach

array.forEach((data,index,arr) =>{
    console.log("arr["+index+"] =" + data);
})
/**
 * forEach 方法为数组中含有有效值的每一项执行一次 callback 函数,那些已删除(使用 delete 方法等情况)或者从未赋值的项将被跳过(不包括那些值为 undefined 或 null 的项)。 callback 函数会被依次传入三个参数:
    数组当前项的值;
    数组当前项的索引;
    数组对象本身;
    添加数组当前项的索引参数,注意callback 函数中的三个参数顺序是固定的,不可以调整。
 */
 
// forEach 遍历的范围在第一次调用callback前就会确定。调用forEach后添加到数组中的项不会被callback访问到。
const arr2 = [];
arr2[0] = "a";
arr2[5] = "b";
arr2[10] = "c";
arr2.word = "HelloWorld";
arr2.forEach((data, index, array) => {
    console.log(data, index, array);
});

四、for-of(ES6新增循环)

  1. 为什么新引入一个for-of循环
    在ES6之前:forEach 不能 break 和 return;
    for-in 缺点更加明显,它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。
  2. for-of可以干什么
    跟 forEach 相比,可以正确响应 break, continue, return。
    for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。
    for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。


嘻嘻,这还是一条分割线

最后纪录一个最近看到的涉及forEach和for-of的大厂面试题:
输出以下代码运行结果,为什么?如果希望每隔 1s 输出一个结果,应该如何改造?注意不可改动 square 方法。

const list = [1, 2, 3]
const square = num => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num * num)
        }, 1000)
    })
} 
function test() {
    list.forEach(async x=> {
        const res = await square(x);
        console.log(res);
    })
}
test()

forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、4、9

// 方法一、把forEach换成普通for循环
async function test() {
    for(let i = 0, long= list.length; i<long; i++){
        const res = await square(list[i])
        console.log(res);
    }
}
// 方法二、更换为for-of循环
async function test() {
    for(x of list){
        const res = await square(x);
        console.log(res);
    }
}
// 方法三、利用promise本身的链式调用
function test() {
    let promise = Promise.resolve();
    list.forEach(x => {
        promise = promise.then(() => square(x)).then((res) => {
            console.log(res);
        })
    })
}