reduce()详解版

1. 求和

typescript 复制代码
const numbers: number[] = [1, 2, 3, 4, 5]; // 定义一个数字数组
const sum: number = numbers.reduce((acc, curr) => acc + curr, 0); 
// 使用 reduce 方法计算数组中所有数字的和,acc 是累加器,从 0 开始,curr 是当前值
console.log(sum); // 输出: 15

计算过程

下面是 reduce 方法的执行过程:

  • 初始值 acc0
  • 第一次迭代:acc = 0 + 1(当前元素)= 1
  • 第二次迭代:acc = 1 + 2(当前元素)= 3
  • 第三次迭代:acc = 3 + 3(当前元素)= 6
  • 第四次迭代:acc = 6 + 4(当前元素)= 10
  • 第五次迭代:acc = 10 + 5(当前元素)= 15

最终,reduce 方法返回的结果是 15,这个结果被赋值给 sum 变量。

2. 扁平化数组

typescript 复制代码
const nestedArray: number[][] = [[1, 2], [3, 4], [5, 6]]; // 定义一个二维数组
const flattenedArray: number[] = nestedArray.reduce((acc, curr) => acc.concat(curr), []); 
// 使用 reduce 方法将二维数组扁平化,acc 是累加器,初始值是空数组,curr 是当前数组
console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5, 6]

计算过程

下面是 reduce 方法的执行过程:

  • 初始值 acc 为 []。
  • 第一次迭代:acc = [] + [1, 2](当前子数组)= [1, 2]
  • 第二次迭代:acc = [1, 2] + [3, 4](当前子数组)= [1, 2, 3, 4]
  • 第三次迭代:acc = [1, 2, 3, 4] + [5, 6](当前子数组)= [1, 2, 3, 4, 5, 6]
  • 最终,reduce 方法返回的结果是 [1, 2, 3, 4, 5, 6],这个结果被赋值给 flattenedArray 变量。

3. 按照属性分组

typescript 复制代码
interface Person { 
  name: string; // 定义人名属性
  age: number;  // 定义年龄属性
} //定义了一个 Person 接口,包含 name 和 age 两个属性,分别是字符串类型和数字类型。

const people: Person[] = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
]; //定义了一个 Person 对象数组 people,其中包含四个人,每个人都有 name 和 age 属性。

const groupedByAge: { [key: number]: Person[] } = people.reduce((acc, curr) => {
  if (!acc[curr.age]) { // 如果累加器对象 acc 中不存在当前年龄 curr.age
    acc[curr.age] = []; // 为该年龄创建一个新的空数组acc[curr.age]。
  }
  acc[curr.age].push(curr); // 将当前 Person 对象 curr 添加到该年龄对应的数组中。
  return acc; // 返回累加器对象
}, {}); // 初始值是一个空对象

console.log(groupedByAge);
/*
输出:
{
  '25': [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
  '30': [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }]
}
*/

计算过程

下面是 reduce 方法的执行过程:

第一次迭代

  • curr{ name: 'Alice', age: 25 }
  • acc{}
  • acc 中没有 25 这个键,所以创建一个新数组:acc = { 25: [] }
  • Alice 添加到 25 对应的数组:acc = { 25: [{ name: 'Alice', age: 25 }] }

第二次迭代

  • curr{ name: 'Bob', age: 30 }
  • acc{ 25: [{ name: 'Alice', age: 25 }] }
  • acc 中没有 30 这个键,所以创建一个新数组:acc = { 25: [{ name: 'Alice', age: 25 }], 30: [] }
  • Bob 添加到 30 对应的数组:acc = { 25: [{ name: 'Alice', age: 25 }], 30: [{ name: 'Bob', age: 30 }] }

第三次迭代

  • curr{ name: 'Charlie', age: 25 }
  • acc{ 25: [{ name: 'Alice', age: 25 }], 30: [{ name: 'Bob', age: 30 }] }
  • acc 中已经有 25 这个键
  • Charlie 添加到 25 对应的数组:acc = { 25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }], 30: [{ name: 'Bob', age: 30 }] }

第四次迭代

  • curr{ name: 'Dave', age: 30 }
  • acc{ 25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }], 30: [{ name: 'Bob', age: 30 }] }
  • acc 中已经有 30 这个键
  • Dave 添加到 30 对应的数组:acc = { 25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }], 30: [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }] }

4. 创建查找映射 (Lookup Map)

typescript 复制代码
interface Product {
  id: number;    // 定义产品ID属性
  name: string;  // 定义产品名称属性
  price: number; // 定义产品价格属性
}//定义了一个 Product 接口和一个包含多个 Product 对象的数组。

const products: Product[] = [
  { id: 1, name: 'Laptop', price: 999 },
  { id: 2, name: 'Phone', price: 699 },
  { id: 3, name: 'Tablet', price: 499 },
]; // 定义一个包含多个产品的数组

const productMap: { [key: number]: Product } = products.reduce((acc, curr) => {
  acc[curr.id] = curr; // 使用产品ID作为键,将产品对象存储在累加器对象中
  return acc; // 返回累加器对象
}, {}); // 初始值是一个空对象

console.log(productMap);
/*
输出:
{
  '1': { id: 1, name: 'Laptop', price: 999 },
  '2': { id: 2, name: 'Phone', price: 699 },
  '3': { id: 3, name: 'Tablet', price: 499 }
}
*/

// 通过ID访问产品
const laptop: Product = productMap[1];
console.log(laptop); // 输出: { id: 1, name: 'Laptop', price: 999 }

计算过程

下面是 reduce 方法的执行过程:

第一次迭代

  • curr{ id: 1, name: 'Laptop', price: 999 }
  • acc{}
  • 使用 1 作为键,将 Laptop 对象存储在 acc 中:acc = { 1: { id: 1, name: 'Laptop', price: 999 } }

第二次迭代

  • curr{ id: 2, name: 'Phone', price: 699 }
  • acc{ 1: { id: 1, name: 'Laptop', price: 999 } }
  • 使用 2 作为键,将 Phone 对象存储在 acc 中:acc = { 1: { id: 1, name: 'Laptop', price: 999 }, 2: { id: 2, name: 'Phone', price: 699 } }

第三次迭代

  • curr{ id: 3, name: 'Tablet', price: 499 }
  • acc{ 1: { id: 1, name: 'Laptop', price: 999 }, 2: { id: 2, name: 'Phone', price: 699 } }
  • 使用 3 作为键,将 Tablet 对象存储在 acc 中:acc = { 1: { id: 1, name: 'Laptop', price: 999 }, 2: { id: 2, name: 'Phone', price: 699 }, 3: { id: 3, name: 'Tablet', price: 499 } }

5. 统计元素出现次数

typescript 复制代码
const fruits: string[] = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']; // 定义一个字符串数组

const fruitCounts: { [key: string]: number } = fruits.reduce((acc, curr) => {
  acc[curr] = (acc[curr] || 0) + 1; // 如果当前水果在累加器对象中不存在,则设置为0,否则累加1
  return acc; // 返回累加器对象
}, {}); // 初始值是一个空对象

console.log(fruitCounts);
/*
输出:
{
  'apple': 3,
  'banana': 2,
  'orange': 1
}
*/

计算过程

下面是 reduce 方法的执行过程:

第一次迭代

  • curr'apple'
  • acc{}
  • acc['apple']undefined,所以设置为 0
  • acc['apple'] = 0 + 1,结果为:acc = { 'apple': 1 }

第二次迭代

  • curr'banana'
  • acc{ 'apple': 1 }
  • acc['banana']undefined,所以设置为 0
  • acc['banana'] = 0 + 1,结果为:acc = { 'apple': 1, 'banana': 1 }

第三次迭代

  • curr'apple'
  • acc{ 'apple': 1, 'banana': 1 }
  • acc['apple'] 已存在,值为 1
  • acc['apple'] = 1 + 1,结果为:acc = { 'apple': 2, 'banana': 1 }

第四次迭代

  • curr'orange'
  • acc{ 'apple': 2, 'banana': 1 }
  • acc['orange']undefined,所以设置为 0
  • acc['orange'] = 0 + 1,结果为:acc = { 'apple': 2, 'banana': 1, 'orange': 1 }

第五次迭代

  • curr'banana'
  • acc{ 'apple': 2, 'banana': 1, 'orange': 1 }
  • acc['banana'] 已存在,值为 1
  • acc['banana'] = 1 + 1,结果为:acc = { 'apple': 2, 'banana': 2, 'orange': 1 }

第六次迭代

  • curr'apple'
  • acc{ 'apple': 2, 'banana': 2, 'orange': 1 }
  • acc['apple'] 已存在,值为 2
  • acc['apple'] = 2 + 1,结果为:acc = { 'apple': 3, 'banana': 2, 'orange': 1 }

6. 组合函数

typescript 复制代码
const add5 = (x: number): number => x + 5;     // 定义一个函数,输入值加5
const multiply3 = (x: number): number => x * 3; // 定义一个函数,输入值乘3
const subtract2 = (x: number): number => x - 2; // 定义一个函数,输入值减2

const composedFunctions: ((x: number) => number)[] = [add5, multiply3, subtract2]; // 定义一个函数数组

const result: number = composedFunctions.reduce((acc, curr) => curr(acc), 10); 
// 使用 reduce 方法将函数依次应用到初始值 10 上,acc 是当前值,curr 是当前函数
console.log(result); // 输出: 43

计算过程

下面是 reduce 方法的执行过程:

初始化

  • composedFunctions: 一个包含三个函数的数组,这些函数依次是add5(加5),multiply3(乘以3),和subtract2(减去2)。
  • 初始值设为10,这个值将作为reduce方法中的累加器(acc)的初始值。

第一次迭代

  • curr 函数是 add5
  • acc (累加器) 的初始值是 10
  • 当调用 add5(acc),将执行 10 + 5,结果为 15
  • 累加器 acc 更新为 15

第二次迭代

  • curr 函数是 multiply3
  • acc 的当前值是 15(上一步的结果)
  • 当调用 multiply3(acc),将执行 15 * 3,结果为 45
  • 累加器 acc 更新为 45

第三次迭代

  • curr 函数是 subtract2
  • acc 的当前值是 45(上一步的结果)
  • 当调用 subtract2(acc),将执行 45 - 2,结果为 43
  • 累加器 acc 最终更新为 43

结束

  • reduce方法完成所有迭代后,最终返回值 43 赋值给result变量。
  • 使用 console.log(result) 打印结果,即打印出了 43

7. 实现简单的类 Redux 状态管理

typescript 复制代码
interface State {
  count: number; // 定义计数器属性
  todos: string[]; // 定义待办事项数组属性
}

interface Action {
  type: string; // 定义动作类型属性
  payload?: any; // 定义可选的负载属性
}

const initialState: State = {
  count: 0,
  todos: [],
}; // 定义初始状态

const actions: Action[] = [
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Learn Array.reduce()' },
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Master TypeScript' },
]; // 定义动作数组

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'INCREMENT_COUNT':
      return { ...state, count: state.count + 1 }; // 增加计数器
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] }; // 添加待办事项
    default:
      return state; // 默认返回当前状态
  }
};

const finalState: State = actions.reduce(reducer, initialState); 
// 使用 reduce 方法将动作数组依次应用到初始状态上
console.log(finalState);
/*
输出:
{
  count: 2,
  todos: ['Learn Array.reduce()', 'Master TypeScript']
}
*/

计算过程

下面是 reduce 方法的执行过程:

初始化

  • initialState: 初始化状态,count0todos为空数组。
  • actions: 一系列动作,包括增加计数器和添加待办事项。

第一次迭代

  • action{ type: 'INCREMENT_COUNT' }
  • state{ count: 0, todos: [] }
  • 根据action.type,执行增加计数器,更新后的状态为 { count: 1, todos: [] }

第二次迭代

  • action{ type: 'ADD_TODO', payload: 'Learn Array.reduce()' }
  • state{ count: 1, todos: [] }
  • 根据action.type,执行添加待办事项,更新后的状态为 { count: 1, todos: ['Learn Array.reduce()'] }

第三次迭代

  • action{ type: 'INCREMENT_COUNT' }
  • state{ count: 1, todos: ['Learn Array.reduce()'] }
  • 根据action.type,再次执行增加计数器,更新后的状态为 { count: 2, todos: ['Learn Array.reduce()'] }

第四次迭代

  • action{ type: 'ADD_TODO', payload: 'Master TypeScript' }
  • state{ count: 2, todos: ['Learn Array.reduce()'] }
  • 根据action.type,执行添加另一个待办事项,更新后的状态为 { count: 2, todos: ['Learn Array.reduce()', 'Master TypeScript'] }

8. 生成唯一值

typescript 复制代码
const numbers: number[] = [1, 2, 3, 2, 4, 3, 5, 1, 6]; // 定义一个包含重复值的数字数组

const uniqueNumbers: number[] = numbers.reduce((acc, curr) => {
  if (!acc.includes(curr)) { // 如果当前值不在累加器数组中
    acc.push(curr); // 将当前值添加到累加器数组中
  }
  return acc; // 返回累加器数组
}, []); // 初始值是一个空数组

console.log(uniqueNumbers); // 输出: [1, 2, 3, 4, 5, 6]

9. 计算平均值

typescript 复制代码
const grades: number[] = [85, 90, 92, 88, 95]; // 定义一个包含多个成绩的数字数组

const average: number = grades.reduce((acc, curr, index, array) => {
  acc += curr; // 将当前值累加到累加器中
  if (index === array.length - 1) { // 如果是数组的最后一个元素
    return acc / array.length; // 计算累加器的平均值
  }
  return acc; // 返回累加器
}, 0); // 初始值是0

console.log(average); // 输出: 90

计算过程

下面是 reduce 方法的执行过程:

第一次迭代

  • curr85

  • acc0

  • acc += curr 操作后 acc 变为 85 第二次迭代

  • curr90

  • acc85

  • acc += curr 操作后 acc 变为 175 ** 第三次迭代**

  • curr92

  • acc175

  • acc += curr 操作后 acc 变为 267

第四次迭代

  • curr88
  • acc267
  • acc += curr 操作后 acc 变为 355

第五次(最后一次)迭代

  • curr95
  • acc355
  • acc += curr 操作后 acc 变为 450
  • 此时检测到是数组的最后一个元素(因index === array.length - 1判断为真),执行 acc / array.length 计算平均值,得到 450 / 5 = 90

结果

  • 最终,reduce方法返回的累加器值为90,这个值被赋给变量average
  • 当使用console.log(average)打印输出时,显示的结果为90,代表这个成绩数组的平均值。
相关推荐
定栓11 小时前
vue3入门- script setup详解上
前端·javascript·vue.js
Lsx-codeShare11 小时前
前端数据可视化:基于Vue3封装 ECharts 的最佳实践
前端·javascript·echarts·vue3·数据可视化
摸着石头过河的石头12 小时前
控制反转 (IoC) 是什么?用代码例子轻松理解
前端·javascript·设计模式
和雍12 小时前
webpack5 创建一个 模块需要几步?
javascript·面试·webpack
百锦再12 小时前
每天两小时学习three.js
开发语言·javascript·学习·3d·three·2d·gbl
小桥风满袖12 小时前
极简三分钟ES6 - const声明
前端·javascript
flyliu13 小时前
继承,继承,继承,哪里有家产可以继承
前端·javascript
机构师13 小时前
<uniapp><日期组件>基于uniapp,编写一个自定义的日期组件
前端·javascript
fury_12313 小时前
vue3:el-date-picker三十天改成第二十九天的23:59:59
前端·javascript·vue.js
小周同学@13 小时前
DOM常见的操作有哪些?
前端·javascript