JS 数组的非破坏性方法:`map`, `filter`, `reduce` 的高级用法

各位观众,各位朋友,早上好(或者下午好,或者晚上好,取决于你们什么时候看)。我是你们今天的JS数组专家,今天咱们来聊聊JS数组的非破坏性方法:mapfilterreduce。别害怕,虽然听起来有点学术,但保证让你们听得进去,学得会,用得上。

啥叫非破坏性方法?

首先,咱们得搞清楚啥叫“非破坏性”。简单来说,就是这些方法在处理数组的时候,不会直接修改原数组,而是返回一个新的数组。这就像你复印一份文件,然后在复印件上涂涂改改,原件还是干干净净的。

map:变形金刚,数组元素的华丽变身

map 方法的作用是对数组中的每个元素应用一个函数,然后返回一个包含所有结果的新数组。你可以把它想象成一个变形金刚,每个元素进去,经过一番操作,变成另外一个样子出来。

  • 基本用法:

    const numbers = [1, 2, 3, 4, 5];
    const doubledNumbers = numbers.map(number => number * 2);
    console.log(doubledNumbers); // 输出: [2, 4, 6, 8, 10]
    console.log(numbers); // 输出: [1, 2, 3, 4, 5] (原数组没变)

    这里,我们把数组 numbers 里的每个数字都乘以 2,生成了一个新的数组 doubledNumbers。注意,numbers 数组本身并没有改变。

  • 进阶用法:提取对象数组的特定属性

    假设我们有一个对象数组,每个对象都有 nameage 属性,我们想提取所有人的名字到一个新的数组里。

    const people = [
      { name: 'Alice', age: 30 },
      { name: 'Bob', age: 25 },
      { name: 'Charlie', age: 35 }
    ];
    
    const names = people.map(person => person.name);
    console.log(names); // 输出: ['Alice', 'Bob', 'Charlie']

    这简直太方便了!不用写循环,一行代码搞定。

  • 更高级的用法:结合索引进行转换

    map 方法还可以接收第二个参数,表示当前元素的索引。这在某些情况下非常有用。

    const numbers = [10, 20, 30];
    const indexedValues = numbers.map((number, index) => `Value at index ${index} is ${number}`);
    console.log(indexedValues); // 输出: ["Value at index 0 is 10", "Value at index 1 is 20", "Value at index 2 is 30"]

    我们用索引和值,构建了一个更有意义的字符串数组。

filter:筛选器,留下你想要的,剔除你不要的

filter 方法的作用是根据指定的条件,筛选数组中的元素,返回一个包含所有符合条件的元素的新数组。你可以把它想象成一个筛选器,只留下你想要的,剔除你不要的。

  • 基本用法:

    const numbers = [1, 2, 3, 4, 5, 6];
    const evenNumbers = numbers.filter(number => number % 2 === 0);
    console.log(evenNumbers); // 输出: [2, 4, 6]
    console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6] (原数组没变)

    我们筛选出了数组 numbers 中的所有偶数。

  • 进阶用法:筛选对象数组

    继续用上面的 people 数组,这次我们想筛选出所有年龄大于 28 岁的人。

    const people = [
      { name: 'Alice', age: 30 },
      { name: 'Bob', age: 25 },
      { name: 'Charlie', age: 35 }
    ];
    
    const olderPeople = people.filter(person => person.age > 28);
    console.log(olderPeople); // 输出: [{ name: 'Alice', age: 30 }, { name: 'Charlie', age: 35 }]

    筛选条件可以是任何你想要的逻辑。

  • 更高级的用法:基于字符串的筛选

    假设我们有一个字符串数组,我们想筛选出所有包含特定字符的字符串。

    const fruits = ['apple', 'banana', 'orange', 'grape'];
    const fruitsWithA = fruits.filter(fruit => fruit.includes('a'));
    console.log(fruitsWithA); // 输出: ["apple", "banana", "orange", "grape"]

    字符串的 includes 方法可以帮助我们判断字符串是否包含某个字符。

reduce:累加器,化零为整的终极武器

reduce 方法的作用是对数组中的每个元素执行一个“reducer”函数(你可以理解为累加器),最终将数组归约为一个单一的值。你可以把它想象成一个累加器,把数组里的所有东西累加起来,得到一个最终的结果。

  • 基本用法:求和

    const numbers = [1, 2, 3, 4, 5];
    const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
    console.log(sum); // 输出: 15
    console.log(numbers); // 输出: [1, 2, 3, 4, 5] (原数组没变)

    这里,accumulator 是累加器,currentValue 是当前元素。0 是初始值。reduce 方法会遍历数组,每次都把 accumulatorcurrentValue 加起来,最终得到总和。

  • 进阶用法:连接字符串

    const words = ['Hello', ' ', 'World', '!'];
    const sentence = words.reduce((accumulator, currentValue) => accumulator + currentValue, '');
    console.log(sentence); // 输出: "Hello World!"

    我们把字符串数组连接成了一个完整的句子。

  • 更高级的用法:统计元素出现次数

    假设我们有一个数组,里面包含各种元素,我们想统计每个元素出现的次数。

    const names = ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'Bob'];
    const nameCounts = names.reduce((accumulator, currentValue) => {
      if (accumulator[currentValue]) {
        accumulator[currentValue]++;
      } else {
        accumulator[currentValue] = 1;
      }
      return accumulator;
    }, {}); // 初始值是一个空对象
    
    console.log(nameCounts); // 输出: { Alice: 2, Bob: 3, Charlie: 1 }

    这里,我们用一个对象来存储每个元素的计数。accumulator 就是这个对象。每次遍历到一个元素,我们就判断它是否已经存在于 accumulator 中。如果存在,就增加计数;如果不存在,就初始化计数为 1。

  • 再高级一点:扁平化数组(二维数组变成一维数组)

    const nestedArray = [[1, 2], [3, 4], [5]];
    const flattenedArray = nestedArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
    console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5]

    我们使用 concat 方法将每个子数组连接到累加器中。

map, filter, reduce 的组合拳:威力加倍

这三个方法可以组合起来使用,形成强大的数据处理流水线。

  • 例子:筛选出年龄大于 28 岁的人的名字,并转换成大写

    const people = [
      { name: 'Alice', age: 30 },
      { name: 'Bob', age: 25 },
      { name: 'Charlie', age: 35 }
    ];
    
    const olderPeopleNames = people
      .filter(person => person.age > 28)
      .map(person => person.name.toUpperCase());
    
    console.log(olderPeopleNames); // 输出: ["ALICE", "CHARLIE"]

    我们先用 filter 筛选出年龄大于 28 岁的人,然后用 map 提取他们的名字,并转换成大写。

总结:表格对比

为了更好地理解这三个方法,我们用一个表格来总结一下:

方法 作用 返回值类型 是否修改原数组 适用场景
map 对数组中的每个元素应用一个函数,返回一个包含所有结果的新数组 数组 数组元素的转换和映射,提取对象数组的特定属性
filter 根据指定的条件,筛选数组中的元素,返回一个包含所有符合条件的元素的新数组 数组 数组元素的筛选和过滤,根据特定条件选择元素
reduce 对数组中的每个元素执行一个“reducer”函数,最终将数组归约为一个单一的值 单一值 数组元素的累加、统计、分组,以及将数组转换为其他数据结构(例如对象)

注意事项:

  • 这些方法都是非破坏性的,所以要记得把结果赋值给一个新的变量。
  • reduce 方法需要一个初始值,这个初始值的类型会影响最终结果的类型。
  • 组合使用这些方法的时候,要注意顺序,不同的顺序可能会导致不同的结果。

结束语:

mapfilterreduce 是 JS 数组处理的利器。掌握它们,可以让你写出更简洁、更高效的代码。希望今天的讲解对大家有所帮助。记住,编程是一门实践的艺术,多写代码,多尝试,才能真正掌握这些方法。下次有机会,我们再聊点更高级的玩法! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注