Javascript
功能性编程
reduce()

Javascript reduce()方法:数组索引,初始值,对象,总和 async, break

JavaScript中的Array.prototype.reduce()方法是最常用的数组方法之一、 它是JavaScript功能性编程方面的一个典型例子。

当你第一次学习编程时,你会被介绍给程序性编程和面向对象编程、 reduce()方法并不总是容易直观地理解。 因此,在这篇文章中,我将从函数式编程到各种现实世界的例子来了解reduce()方法。

1. JavaScript函数式编程简述

函数式编程是一种编程范式,指的是一种基于数学意义上的函数来设计程序行为的技术。

在函数式编程中,只要你把相同的值放入一个函数中,你就应该总是得到相同的结果。 遵循这一原则的函数被称为纯函数,在其行为中不应该改变任何状态值。 如果它改变了,那就叫做边际效应,函数式编程的目的是将它们降到最低。

它还强调了状态和数据的**不变性,因此,与其改变一个数据结构的值 我们设计我们的程序来创建新的数据结构。 这意味着,如果你想把数组[1, 2, 3]改为[1, 2, 4],你就创建一个新的数组[1, 2, 4]。 这样设计的程序在并发编程等方面具有强大的优势,因为这些数据是同时访问的。

最后,JavaScript将函数视为一等公民--可以在任何地方使用的对象:变量、参数、返回值,等等、 它是一种很好的函数式编程的语言。 因此,许多JS开发者使用函数式编程来编写可维护和可靠的代码。

被定义为函数式的函数有

const add = (a, b) => a + b;
 
const result = add(1, 2); // Output is always 3

1.1. 为什么reduce()方法对函数式编程很重要?

reduce()方法坚持了JavaScript中函数式编程的原则,因为它在不修改现有数组的情况下对数组进行操作。 当你把一个数组减少到一个单一的值或者根据数组中的元素累积结果时,这一点尤其正确,因为你不必在函数之外管理累积值。 它也没有副作用。 因此,它是编写更干净、更可维护的代码的一个基本方法。

2. 了解reduce()方法:初始化、索引和累加器

reduce()方法是一个数组方法,它遍历一个数组,对数组中的每个元素执行所提供的函数(reducer),产生一个单一地输出值。

其基本语法是

array.reduce(reducerFunction, initialValue);
 
// reducerFunction
array.reduce(((accumulator, currentValue, currentIndex, array) => returnValue), initialValue);

initialValue是初始值,将被添加到reducerFunction的返回值。 使用0来获得数字的总和,""来获得最终的字符串,等等。

reducerFunction是一个回调函数,需要四个参数。

  • accumulator (accumulator) - 累积对所有元素应用该函数的返回值,直到前一个索引。
  • currentValue (currentValue) - 当前应用该函数的项目的值。
  • index (currentIndex) - 当前元素的数组索引(可选)。
  • array (array) - 对数组本身的引用(可选)

让我们看一下一个简单的例子,看看每个参数会有什么值。

const numbers = [ 1, 2, 3 ]
 
numbers.reduce((accumulator, currentValue, currentIndex, array) => {
    console.log(`accumulator: ${accumulator}, currentValue: ${currentValue}, currentIndex: ${currentIndex}, array: ${array} `)
    return accumulator + currentValue
}, 0);
 
// Output:
// accumulator: 0, currentValue: 1, currentIndex: 0, array: 1,2,3
// accumulator: 1, currentValue: 2, currentIndex: 1, array: 1,2,3
// accumulator: 3, currentValue: 3, currentIndex: 2, array: 1,2,3
// 6

在第一次调用索引为0的reducer函数时,累积器的初始值为0。 减速器函数将当前值1添加到累加器的值中,并将其返回,成为新的累加器值。 这个过程重复进行,直到reduce()函数的最终结果返回最终的累加器值,6

换句话说,要解开上述例子的行为,它看起来是这样的

const numbers = [ 1, 2, 3 ]
const reducerFunction = element => element
 
const returnValue = reducerFunction(numbers[0]) + reducerFunction(numbers[1]) + reducerFunction(numbers[2])
 
console.log(returnValue)
// Output: 6

2.1. 小心break和continue关键字

大多数数组方法, 包括reduce()函数, 都不支持操纵遍历操作的break,``continue{:js}关键字。 如果你不想对当前元素做任何操作,比如使用continue关键字,你可以使用条件语句来原样返回累积值。 在下面的例子中,我们通过了3,而没有进行累积。

const numbers = [ 1, 2, 3 ]
const reducerFunction = (accumulator, element) => {
    console.log(`누산기: ${accumulator}, 현재값: ${element}`)
    if (element === 3) {
        return accumulator
    }
    return accumulator + element
}
 
numbers.reduce(reducerFunction, 0)
// Output: 3

3. 如何用它来操作对象

reduce()方法对于访问对象的属性并对其进行各种操作也很有用。 你可以直接访问对象的属性,或者使用像Object. entries()这样的辅助方法。

让我们逐一来看看它们。

const people = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 30 },
    { name: 'Charlie', age: 25 },
    { name: 'David', age: 30 }
];
 
const groupedByAge = people.reduce((accumulator, currentValue) => {
    const ageGroup = currentValue.age;
    accumulator[ageGroup] = accumulator[ageGroup] || [];
    accumulator[ageGroup].push(currentValue);
    return accumulator;
}, {});
 
console.log(groupedByAge);
// Output:
// {
//  '25': [
//      { name: 'Alice', age: 25 },
//      { name: 'Charlie', age: 25 }
//  ],
//  '30': [
//      { name: 'Bob', age: 30 },
//      { name: 'David', age: 30 }
//  ]
// }

上面的例子访问了一个对象的age属性,并执行了一个任务,根据对象的年龄将其分为两组。

const grades = {
    math: 90,
    science: 80,
    history: 85,
    english: 95
};
 
const totalScore = Object.entries(grades).reduce((accumulator, [subject, score]) => {
    return accumulator + score;
}, 0);
 
console.log(totalScore); // Output: 350

如果你需要操作一个对象的多个值,你可以使用Objext. entries()方法。 这个方法将对象的属性和属性值作为一个单一的数组来操作,如上图所示。 这使得我们也可以在对象上使用reduce()函数,它看起来像这样、 解构赋值,如[subject, score],可以用来编写更简洁的代码。

4. 现实世界中的应用实例,如删除重复的内容

JS filter()函数--3.用filter()进行重复删除一节中,我们也看到了重复删除技术的代码实例。 通过reduce()函数,你也可以很容易地编写代码,从数组中删除重复的元素。

让我们从代码开始。

const array = [1, 2, 3, 2, 4, 3, 5, 1];
 
const uniqueArray = array.reduce((accumulator, element) => {
  if (!accumulator.includes(element)) {
    accumulator.push(element);
  }
  return accumulator;
}, []);
 
console.log(uniqueArray);
// Output: [1, 2, 3, 4, 5]

上面的代码将初始值设置为空数组[],如果当前元素不存在于accumulator{js}数组中,则向其添加一个新元素。 使用Array.includes()方法来确定包含。

将此与JS filter()函数--4.用includes()定义回调函数一节中使用的String.includes()方法进行对比。

你可以看到,由reduce函数返回的结果数组只拉出不重复的元素。

5. 使用异步回调函数

我们在JS map()方法--6.异步回调:异步回调函数中看到如何在数组方法中使用异步回调函数。 map()方法,以及Promise.all()方法,允许我们编写异步+并发工作的代码。

然而,reducer方法不能用于异步回调函数,因为没有办法支持异步行为。 为了解决这个问题,我们需要定义自己的自定义reduce函数,使用async await关键字和Promise对象异步工作。

让我们从示例代码开始。

const array = [1, 2, 3, 4, 5];
 
const asyncReduce = async (array, callback, initialValue) => {
  let accumulator = initialValue;
 
  for (const element of array) {
    accumulator = await callback(accumulator, element);
  }
 
  return accumulator;
};
 
const sumAsync = async (a, b) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(a + b);
    }, 1000);
  });
};
 
const calculateSum = async () => {
  const sum = await asyncReduce(array, sumAsync, 0);
  console.log(sum);
};
 
calculateSum();
// Output: 15

例子中的asyncReduce()函数通过for of语法遍历数组,调用异步回调函数,并await等待所有操作完成。 任意的异步回调函数sumAsync()等待一秒,然后返回加法的结果。 最后,calculateSum()函数作为主函数,实际执行asyncReduce()函数。

如果你运行这段代码,在一定时间后,你将得到所有数字的总和,15

6. 总结

JavaScript的reduce()方法是函数式编程的一个强大而通用的工具。 它允许你对数组进行操作,同时保持不变性和简单性等原则。 明智地将它与其他数组方法结合起来使用,并确保当你写代码时,它是直观的和可读的。

copyright for Javascript reduce

© 2023 All rights reserved.