likes
comments
collection
share

JS filter、map、reduce等十三种遍历数组方法详细总结🔥

作者站长头像
站长
· 阅读数 14

我正在参加「掘金·启航计划」

前言

在日常工作中,无论是原生开发还是使用如 vuereact 等框架开发,使用 数组 遍历操作数据都是十分频繁的,因此,本文总结一下,方便自己和大家参考。内容较多,可以点赞+收藏,方便使用时查阅!

前置知识补充

1. 什么是JS迭代方法?

JS迭代方法可以被视为一种更简洁和功能丰富的遍历循环方式。它们提供了一种更高级、更抽象的方法来处理数组或类数组对象的元素,而无需使用传统的for循环或while循环。

迭代方法封装了遍历的逻辑,并提供了更多的操作选项,例如映射、筛选、聚合等,使代码更易读、简洁,并且能够以函数式编程的方式来处理数据。

2. 什么是高阶函数?

高阶函数是指能够接受一个或多个函数作为参数,并返回一个新函数作为结果的函数。将函数作为数据进行操作,使得函数具有更高的抽象和灵活性,简而言之,高阶函数是能够操作其他函数的函数。

// 高阶函数示例:接受函数作为参数
function greet(name, callback) {
  const message = `Hello, ${name}!`;
  callback(message);
}

// 回调函数
function printMessage(message) {
  console.log(message);
}

// 调用高阶函数,并传递回调函数作为参数
greet('John', printMessage);

遍历方法目录

  1. for循环(稍许繁琐,可中断和跳过)
  2. for-of(简化for循环,更加灵活,无需索引)
  3. forEach (适合对每个元素执行操作,没有返回值)
  4. some(检查数组中是否至少存在一个元素满足指定的条件)
  5. every(检查数组中的所有元素是否都满足指定的条件)
  6. filter(根据指定的条件筛选出数组中满足条件的元素)
  7. find(查找满足指定条件的第一个元素)
  8. findIndex(查找满足指定条件的第一个元素索引)
  9. findLast(查找满足指定条件的最后一个元素)
  10. findLastIndex(查找满足指定条件的最后一个元素索引)
  11. map(数据不符合展示要求,需要处理时)
  12. reduce(强大灵活: 数值计算、数组变换、扁平化数组...)
  13. reduceRight(跟上面方法区别就是从后往前遍历)

1. for循环

for 循环对数组进行遍历是最常见和最基本的方法之一,虽然简单灵活但是语法冗长,容易出错,可读性也不高。现在基本被高阶函数替代,如 forEach、map、filter、reduce

示例:

const array = [1, 2, 3]; 
for (let i = 0; i < array.length; i++) { 
    console.log(array[i]); 
}

2. for-of

for...of 语句用于遍历可迭代对象,它提供了一种更简单和直观的方式来迭代元素,而无需使用索引

示例:

// 迭代映射对象
const map = new Map([["name", "John"], ["age", 30]]); 
for (const [key, value] of map) { 
    console.log(key, value); 
}
// name John
// age 30

3. forEach*

  • 参数: forEach(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: undefined
  • 是否改变原数组: 可以改变原数组
  • 使用时机: 用于对数组中的每个元素执行指定的操作

特性:

  • 除非抛出异常,否则没有办法停止或中断,不能使用break 会报错
  • 不可以直接修改元素,但是可以修改元素的属性

示例:

const numbers = [1, 2, 3, 4, 5];

// 除非抛出异常,否则没有办法停止或中断。不能使用break。返回值为undefined
const value = numbers.forEach((item, index, arr) => {
    console.log(item, index, arr)å
    // if(item === 2) break; // 报错 SyntaxError: Illegal break statement
    return item // 无效
})

// 不可以直接修改元素,但是可以修改元素的属性
numbers.forEach((item, index, arr) => {
    item = item * 2; // 这里只是修改了形参item的值,并不会修改原数组
    arr[index] = item * 2; // 通过修改属性修改原数组值
})

4. some

  • 参数: some(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: true|false
  • 是否改变原数组:不改变原数组
  • 使用时机: 用于检查数组中是否至少存在一个元素满足指定的条件

特性:

  • 除非抛出异常或者返回 true ,否则没有办法停止或中断,不能使用break 会报错
  • 如果有一个元素符合条件,则循环不再继续,直接返回 true,否则返回 false

示例:

const numbers = [1, 2, 3, -4, 5]; 
const hasNegative = numbers.some(num => num < 0); 
console.log(hasNegative);

5. every

  • 参数: every(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: true|false
  • 是否改变原数组: 不改变原数组
  • 使用时机: 用于检查数组中的所有元素是否都满足指定的条件

特性:

  • 除非抛出异常或者返回 false ,否则没有办法停止或中断,不能使用break 会报错
  • 如果有一个元素符合条件,则循环不再继续,直接返回 false,否则返回 true

示例:

// 必须都满足才返回true,否则立刻终止循环
const numbers = [1, 2, 3, 4, 5]; 
const allPositive = numbers.every(num => num > 0); 
console.log(allPositive); // true

// 数组排序判断:在对数组进行排序或筛选时,可以使用 `every` 方法进行判断。
// 例如,检查数组是否已按升序排序。
const sortedNumbers = [1, 2, 3, 4, 5];
const isSortedAscending = sortedNumbers.every((num, index, arr) => {
  if (index === 0) {
    return true;
  }
  return num >= arr[index - 1];
});
console.log(isSortedAscending);  // true

someevery 刚好相反

  • some 只要有一个符合条件的就返回 true

  • every 必须全部满足条件才返回 true

6. filter*

  • 参数: filter(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: []|[...ele]
  • 是否改变原数组: 不改变原数组
  • 使用时机: 用于根据指定的条件筛选出数组中满足条件的元素

特性:

  • 除非抛出异常,否则没有办法停止或中断,不能使用break 会报错
  • 筛选掉不符合条件的元素,返回符合条件的元素

示例:

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers);  // [2, 4]

// 对象属性筛选,筛选出成年用户
const users = [ 
    { name: 'John', age: 25 }, 
    { name: 'Jane', age: 18 }, 
    { name: 'Bob', age: 30 }
]; 
const adultUsers = users.filter(user => user.age >= 18); 
console.log(adultUsers); // [{ name: 'John', age: 25 }, { name: 'Jane', age: 18 }, { name: 'Bob', age: 30 }]

7. find

  • 参数: find(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: undefined|符合条件的元素
  • 是否改变原数组: 不改变原数组
  • 使用时机: 用于在数组中查找满足指定条件的第一个元素

特性:

  • 除非抛出异常或者找到 符合条件的元素 ,否则没有办法停止或中断,不能使用break 会报错
  • 找到第一个符合条件的元素返回并中断循环,否则所有元素都循环一遍,返回undefined

示例:

const numbers = [1, 2, 3, -4, 5];
const negativeNumber = numbers.find(num => num < 0);
console.log(negativeNumber);  // -4

const users = [ 
    { name: 'John', age: 25 }, 
    { name: 'Jane', age: 18 }, 
    { name: 'Bob', age: 30 } 
]; 
const user = users.find(user => user.name === '张三');
console.log(user); // undefined

filterfind的区别(面试考点*):

  1. 返回值:

  • filter函数:返回一个由满足条件的元素组成的新数组,如果没有元素满足条件,则返回空数组。

  • find函数:返回满足条件的第一个元素,如果没有找到满足条件的元素,则返回undefined。

  1. 用途:

  • filter函数:用于筛选出数组中符合条件的元素,并返回一个新数组。适用于需要筛选多个元素的情况。
  • find函数:用于查找数组中满足条件的第一个元素,并返回该元素。适用于查找单个元素的情况。

8. findIndex

find类似。只不过是查找元素下标,没有找到符合条件的就返回 -1

示例:

const array = [1, 2, 3, 4, 5];
const foundIndex = array.findIndex((element) => element > 3);
const noIndex = array.findIndex((element) => element > 10);
console.log(foundIndex); // 输出结果:3
console.log(noIndex); // 输出结果:-1

9. findLast

findLast 方法用于在数组中从后往前查找满足条件的元素,并返回该元素。和 find 方法类似,但是从数组的末尾开始搜索,找到满足条件的最后一个元素

应用场景:

  • 查找数组中的最后一个满足特定条件的元素。
  • 需要从数组的末尾开始搜索并获取满足条件的元素。

示例:

const numbers = [1, 2, 3, 4, 5];
const lastEvenNumber = numbers.findLast(num => num % 2 === 0); 
console.log(lastEvenNumber); // 4

10. findLastIndex

findLastIndex 方法用于在数组中从后往前查找满足条件的元素,并返回该元素的索引。和 findIndex 方法类似,但是从数组的末尾开始搜索,找到满足条件的最后一个元素的索引。

应用场景:

  • 查找数组中最后一个满足特定条件的元素的索引。
  • 需要从数组的末尾开始搜索并获取满足条件的元素的索引。

注意: 找到的最后一个满足条件的索引不是从数组末尾开始的,而是从数组第一个元素开始的索引

示例:

const numbers = [1, 2, 3, 4, 5];
const lastEvenNumberIndex = numbers.findLastIndex(num => num === 4);
console.log(lastEvenNumberIndex);  // 3

11. map*

  • 参数: map(callbackFn[, thisArg]),接受一个回调函数作为参数,回调函数可以接受三个参数:element正在处理的当前元素。、index当前元素的索引、array被遍历的数组。
  • 返回值: 返回一个新数组,该数组由回调函数的返回值组成。
  • 是否改变原数组: 不改变原数组
  • 使用时机: 返回的数据不符合我们的要求,可以使用 map 把数据处理成我们想要的

特性:

  • 数组长度保持一致:新数组的长度与原数组相同,每个元素对应原数组中的一个元素。新数组元素受返回值影响
  • 除非抛出异常,否则没有办法停止或中断,不能使用break 会报错

示例:

const fruits = ['apple', 'banana', 'orange']; 
const capitalizedFruits = fruits.map(fruit => fruit.toUpperCase()); 
console.log(capitalizedFruits); // ['APPLE', 'BANANA', 'ORANGE']

12. reduce*

  • 参数: 接收两个参数
    • 参数一:是一个函数,函数有四个参数,分别是:上一次的值,当前值,当前值的索引,数组
    • 参数二:可选的基底值,可以设置一个数字参与求和运算
  • 返回值: 最终累积结果:reduce方法返回最终的累积结果,可以是任意数据类型,如数字、字符串、对象或数组。
  • 是否改变原数组: 不改变原数组
  • 使用时机: 用于各种集合处理和聚合的情况,可以根据需求定义自定义的合并操作
const count = [0, 1, 2, 3, 4].reduce((prev, curr, index, array) => prev + curr);
console.log(count); // 10

callback 被调用四次,每次调用的参数和返回值如下表:

JS filter、map、reduce等十三种遍历数组方法详细总结🔥

如果你打算提供一个初始值作为reduce()方法的第二个参数,以下是运行过程及结果:

const count = [0, 1, 2, 3, 4].reduce((prev, curr, index, array) => prev + curr, 10);
console.log(count); // 20

JS filter、map、reduce等十三种遍历数组方法详细总结🔥

应用1: 求和

const numbers = [1, 2, 3, 4, 5]; 
const sum = numbers.reduce((acc, curr) => acc + curr, 0); 
console.log(sum); // 15

应用2: 数组扁平化

const nestedArray = [[1, 2], [3, 4], [5, 6]]; 
const flattenedArray = nestedArray.reduce((acc, curr) => acc.concat(curr), []); 
console.log(flattenedArray); // [1, 2, 3, 4, 5, 6]

应用3: 数组元素的最大值和最小值

const numbers = [10, 5, 8, 3, 12]; 
const maxNumber = numbers.reduce((max, curr) => Math.max(max, curr), Number.MIN_SAFE_INTEGER); 
console.log(maxNumber); // 12 

const minNumber = numbers.reduce((min, curr) => Math.min(min, curr), Number.MAX_SAFE_INTEGER); 
console.log(minNumber); // 3

应用4:求平均值

const scores = [85, 90, 76, 92, 88]; 
const averageScore = scores.reduce((sum, curr) => sum + curr, 0) / scores.length; 
console.log(averageScore); // 86.2

13. reduceRight

reduceRight 和 reduce 都是对数组元素进行累计操作,这两个之间的区别在于迭代方向,reduce 从数组左侧开始迭代,reduceRight从数组右侧。

reduceRight 需要从数组的尾部开始处理元素以满足特定的需求,如字符串反转,处理树结构等...

应用1: 字符串反转

const str = 'Hello, World!'; 
const reversedStr = [...str].reduceRight((acc, curr) => acc + curr, ''); 
console.log(reversedStr); // "!dlroW ,olleH"

应用2: 扁平化树结构

const tree = {
    name: 'A',
    children: [
        { 
            name: 'B',
            children: [
                { name: 'D', children: [] },
                { name: 'E', children: [] }
            ]
        },
        { name: 'C', children: [] }
    ] 
};

function flattenTree(tree) {
  return [tree, ...tree.children.reduceRight((acc, curr) => {
    return [...acc, ...flattenTree(curr)];
  }, [])];
}

const flattenedTree = flattenTree(tree);
console.log(flattenedTree);
// [
//   { name: 'A', children: [...] },
//   { name: 'B', children: [...] },
//   { name: 'D', children: [] },
//   { name: 'E', children: [] },
//   { name: 'C', children: [] }
// ]

总结🎉

  1. 特定的循环,并且需要中断和跳过可使用 for

  2. 无需返回值可使用 forEach、for...of

  3. 检查是否满足指定条件而无需使用元素可使用 some、every

  4. 筛选符合条件的元素可使用filter、find、findLast

  5. 找到符合条件的元素索引可使用 findIndex、findLastIndex

  6. 需要对不符合展示条件的数据继续再处理时可使用map

  7. 需要依赖数组元素上次结果,如累计可使用 reduce、reduceRight