JS数组reduce方法初识

JS数组reduce方法初识

前言:

从ES5到ES6之后有加了许多数组方法,本文来彻底了解一下reduce方法以及它的执行原理

reduce

具体了解一下reduce这个方法是用来干什么的,官方给出的解释也是用来求和的高阶函数

看一下它的语法

scss 复制代码
reduce(callbackFn) //没有初始值
reduce(callbackFn, initialValue) //设定初始值

来说明一reduce这几个参数的作用

js 复制代码
const initialValue = 0;
arr.reduce((acc, cur, curIndex, array)=>{

},initialValue)

acc : 通常被称为累加器, 上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 则为指定的值,否则为 array[0] 的值。

cur : 当前元素的值。在第一次调用时,如果指定了 initialValue,则为 array[0] 的值,否则为 array[1]

curIndex : currentValue 在数组中的索引位置。在第一次调用时,如果指定了 initialValue 则为 0,否则为 1

array : 调用了 reduce() 的数组本身,这句话是官方给出的,稍有一些绕,说白了就是调用reduce方法的该数组

js 复制代码
//数组求和
const sum = arr.reduce((acc, cur, curIndex, array) => {
  console.log(acc, cur, curIndex, array, 'acc, cur, curIndex, array');
  return acc + cur
},)
//计算数组中每个元素出现的次数
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let countedNames = names.reduce(function (allNames, name) {
  console.log(allNames, 'allNames');

  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
console.log(countedNames, 'countedNames');

异常:

若未提供initialValue则会抛出错误异常,如下图错误

reduce有无初始值运行机制:

无初始值的 reduce() 代码:

js 复制代码
const array = [15, 16, 17, 18, 19];

function reducer(accumulator, currentValue, index) {
  const returns = accumulator + currentValue;
  console.log(
    `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,
  );
  return returns;
}

array.reduce(reducer);

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

accumulator currentValue index 返回值
第一次调用 15 16 1 31
第二次调用 31 17 2 48
第三次调用 48 18 3 66
第四次调用 66 19 4 85

array 参数在整个过程中始终不会改变------它始终是 [15, 16, 17, 18, 19]reduce() 返回的值将是最后一次回调返回值(85)。

有初始值的 reduce() 代码:

ini 复制代码
[15, 16, 17, 18, 19].reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  10,
);

回调函数会被调用五次,每次调用的参数和返回值如下表:

accumulator currentValue index 返回值
第一次调用 10 15 0 25
第二次调用 25 16 1 41
第三次调用 41 17 2 58
第四次调用 58 18 3 76
第五次调用 76 19 4 95

这种情况下 reduce() 返回的值是 95

reduce是js的高阶函数,它的功能很强大,大家可以根据以下例子来自行深究在项目中的高级用法

求对象数组中值的总和

为了对包含在对象数组中的值进行求和,必须 提供一个 initialValue,以便每个项都通过回调函数处理

js 复制代码
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
const sum = objects.reduce(
  (accumulator, currentValue) => accumulator + currentValue.x,
  0,
);

console.log(sum); // 6

展平嵌套数组

js 复制代码
const flattened = [
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened 的值是 [0, 1, 2, 3, 4, 5]

统计对象中值的出现次数

js 复制代码
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];

const countedNames = names.reduce((allNames, name) => {
  const currCount = allNames[name] ?? 0;
  return {
    ...allNames,
    [name]: currCount + 1,
  };
}, {});
// countedNames 的值是:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

按属性对对象进行分组

js 复制代码
const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    const curGroup = acc[key] ?? [];

    return { ...acc, [key]: [...curGroup, obj] };
  }, {});
}

const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }

使用展开语法和 initialValue 连接包含在对象数组中的数组

js 复制代码
// friends------一个对象数组,其中对象字段"books"是最喜欢的书的列表
const friends = [
  {
    name: "Anna",
    books: ["Bible", "Harry Potter"],
    age: 21,
  },
  {
    name: "Bob",
    books: ["War and peace", "Romeo and Juliet"],
    age: 26,
  },
  {
    name: "Alice",
    books: ["The Lord of the Rings", "The Shining"],
    age: 18,
  },
];

// allbooks------列表,其中包含所有朋友的书籍和 initialValue 中包含的附加列表
const allbooks = friends.reduce(
  (accumulator, currentValue) => [...accumulator, ...currentValue.books],
  ["Alphabet"],
);
console.log(allbooks);
// [
//   'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
//   'Romeo and Juliet', 'The Lord of the Rings',
//   'The Shining'
// ]

数组去重

js 复制代码
const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
  if (!accumulator.includes(currentValue)) {
    return [...accumulator, currentValue];
  }
  return accumulator;
}, []);

console.log(myArrayWithNoDuplicates);

使用reduce替换filter和map

个人认为这个用法会比较多一点,大家平时也可以自己多尝试

使用 filter()map()会遍历数组两次,但是你可以使用 reduce() 只遍历一次并实现相同的效果,从而更高效。(如果你喜欢使用 for 循环,你可以在遍历一次时使用 forEach()进行过滤和映射。)

js 复制代码
const numbers = [-5, 6, 2, 0];

const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
  if (currentValue > 0) {
    const doubled = currentValue * 2;
    return [...accumulator, doubled];
  }
  return accumulator;
}, []);

console.log(doubledPositiveNumbers); // [12, 4]

按顺序运行Promise

js 复制代码
/**
 * 链接一系列 Promise 处理程序。
 *
 * @param {array} arr------一个 Promise 处理程序列表,每个处理程序接收前一个处理程序解决的结果并返回另一个 Promise。
 * @param {*} input------开始调用 Promise 链的初始值
 * @return {Object}------由一系列 Promise 链接而成的 Promise
 */
function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input),
  );
}

// Promise 函数 1
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5);
  });
}

// Promise 函数 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2);
  });
}

// 函数 3------将由 `.then()` 包装在已解决的 Promise 中
function f3(a) {
  return a * 3;
}

// Promise 函数 4
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4);
  });
}

const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10).then(console.log); // 1200

使用函数组合实现管道

js 复制代码
// 组合使用的构建块
const double = (x) => 2 * x;
const triple = (x) => 3 * x;
const quadruple = (x) => 4 * x;

// 函数组合,实现管道功能
const pipe =
  (...functions) =>
  (initialValue) =>
    functions.reduce((acc, fn) => fn(acc), initialValue);

// 组合的函数,实现特定值的乘法
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);

// 用例
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240

稀疏数组中使用reduce

reduce() 会跳过稀疏数组中缺失的元素,但不会跳过 undefined 值。

js 复制代码
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7

console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN

在非数组对象上调用reduce

reduce() 方法读取 thislength 属性,然后访问每个整数索引。

js 复制代码
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};

console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));
// 9

结语:以上是我结合和MDN官方文档的demo做出的总结,也是作为自己对reduce的一个深度理解,后续也会持续更新其他知识点

相关推荐
许___16 小时前
Vue使用原生方式把视频当作背景
前端·javascript·vue.js
萌萌哒草头将军16 小时前
尤雨溪强烈推荐的这个库你一定要知道 ⚡️⚡️⚡️
前端·vue.js·vite
2401_8784545316 小时前
Vue 核心特性详解:计算属性、监听属性与事件交互实战指南
前端·vue.js·交互
GISer_Jing17 小时前
Next.js数据获取演进史
java·开发语言·javascript
1024小神17 小时前
uniapp+vue3+vite+ts+xr-frame实现ar+vr渲染踩坑记
前端
测试界清流17 小时前
基于pytest的接口测试
前端·servlet
知识分享小能手17 小时前
微信小程序入门学习教程,从入门到精通,自定义组件与第三方 UI 组件库(以 Vant Weapp 为例) (16)
前端·学习·ui·微信小程序·小程序·vue·编程
trsoliu18 小时前
多仓库 Workspace 协作机制完整方案
前端
啦工作呢18 小时前
数据可视化 ECharts
前端·信息可视化·echarts
NoneSL18 小时前
Uniapp UTS插件开发实战:引入第三方SDK
前端·uni-app