全部 / 前端 / 技术 · 2022年6月27日 0

45 – 46 – 47 – Array Map & Array Reduce & Array Filter

原文地址:https://dev.to/bhagatparwinder/array-map-explained-6de

map() 方法为每一个数组的元素应用一个函数然后返回一个新的数组。新的数组和原数组的长度一样。map() 的回调函数接受参数,第一个参数代表当前处理的元素。

当你需要为数组中的每个元素进行操作时,在这种情况下 map() 很有帮助。你可能会给每个元素进行双倍处理:

const arr = [2, 4, 9, 22];
const map = arr.map(x => x * 2);
console.log(map); // [ 4, 8, 18, 44 ]

你使用 map 同样可以获取当前数组元素的下标,回调函数的第二个参数是下标:

const arr = [2, 4, 9, 22];
const map = arr.map((value, index) => value * index); // multiplying array value with its index
console.log(map); // [ 0, 4, 18, 66 ]

若获取下标还不够,你还可以获取到原数组。

map 方法和 this 关键字

有时候你需要为 map() 指定 this 上下文,map 支持第二个参数传入 this

const util = {
    firstName: "John",
    lastNames: ["Wick", "Malcolm", "Smith"],
    randomNameGenerator: function () {
        const newNames = this.lastNames.map(function (surname) {
            return (`${this.firstName} ${surname}`); // we can access first name provided by this object
        }, this); // passing reference to this object
        return newNames;
    }
}

console.log(util.randomNameGenerator()); // [ 'John Wick', 'John Malcolm', 'John Smith' ]

什么时候不使用 map

  • 若你不打算使用返回的新数组或回调函数没有返回值,则不要使用 map。若你想更新原数组,可以使用 forEachfor...of
  • 不要和一般情况下只传递一个参数但可以接收更多参数的内置函数一起使用。

我来举一个例子:

const arr = ["2", "4", "9"];
const map = arr.map(parseInt);
console.log(map);

你可能期望结果为 [2,4,9],每个元素通过 parseInt 处理后得到数字类型。你会惊讶的发现结果为:[2,NaN,NaN]。记住 parseInt 接收了三个参数:element,index 和 array。所以作为第二个和第三个元素,index 是 1 和 2 导致 parseInt 返回 NaN。


reduce() 方法为输入数组的每个元素应用 reducer 函数并返回一个值。它接受一个可传入数组、累加器、当前值和下标作为参数的回调函数。

当你需要为数组中的元素执行一些操作并返回一个值时 reduce() 会很有用。你或许需要把数组中的元素相加。

const arr = [2, 4, 9, 22];
const reduce = arr.reduce((final, current) => final + current);
console.log(reduce); // 37 (2 + 4 + 9 + 22)

final 就是最初数组中所有元素相加的累加器,current 代表当前循环的值。

你同样可以获取当前数组循环的下标,回调函数的第三个参数作为下标。

const arr = [2, 4, 9, 22];
// we will add all numbers as well as their indices
const reduce = arr.reduce((final, current, index) => final + current + index);
console.log(reduce); // 43 (2 + 0 + 4 + 1 + 9 + 2 + 22 + 3)

如果获取下标还不够,你还可以从第四个参数获取原始数组。

我们将再举一个例子来看看 reduce 的强大之处以及使用的场景,我们将会不借助 flat 方法来使数组展开。

const flatArray = (arr) => {
    let output = [];
    return arr.reduce((final, value) => {
        // use recursion, if value then concat to our final array else call flatArray again with the child array
        return final.concat(Array.isArray(value) ? flatArray(value) : value);
    }, output);
}

const input = [1, 2, 3, [10, 11, 12], 21, 22, 23, [31, 32, 33, 34], [41, 42]];
console.log(flatArray(input)); // [ 1, 2, 3, 10, 11, 12, 21, 22, 23, 31, 32, 33, 34, 41, 42 ]

reduce 有第二个参数但是它并不像 map 一样是 this 上下文作为第二个参数,而是 reduce 开始的初始值。

看看上面的例子我们把 output 作为初始值。若没有提供初始值,数组的第一个元素将作为初始值,数组将从第二个元素开始循环。

filter() 方法返回一个由满足测试条件的所有元素组成的新数组。它接受一个支持输入当前元素、下标和原始数组的回调函数。最后两个参数(下标和数组)是可选的。

当你需要从输入的数组中挑选出满足你标准的元素时 filter() 会很有帮助。或许你可能想要数组中所有的偶数或字符串长度大于 6 的元素。

const names = ["Parwinder", "Leah", "Lauren", "Eliu", "Robert", "George", "Eric"];
const output = names.filter(name => name.length >= 6);

console.log(output); // [ 'Parwinder', 'Lauren', 'Robert', 'George' ]
console.log(names); // [ 'Parwinder', 'Leah', 'Lauren', 'Eliu', 'Robert', 'George', 'Eric' ]

filter() 并不改变原始数组,就像上面的例子一样原始数组不会改变。

filter() 方法中同样可以获取数组的下标,回调函数的第二个参数为下标。

const arr = [1, 2, 4, 9, 22, 75, 16];
const filter = arr.filter((current, index) => (current % index === 0));
// return values that are divisible by the index they are on
console.log(filter); // [ 2, 4, 9, 75 ]

若下标还不够,你还可以通过第三个参数获取原始数组。

filter() 还有第二个参数:this。它正像 map() 方法一样,指定了回调函数的上下文。