Javascript数组方法中,相比mapfilterforEach等常用的迭代方法,reduce常常被我们所忽略,今天一起来探究一下reduce在我们实战开发当中,能有哪些妙用之处,下面从reduce语法开始介绍。

语法

array.reduce(function(accumulator, arrayElement, currentIndex, arr), initialValue)
复制代码

若传入初始值,accumulator首次迭代就是初始值,否则就是数组的第一个元素;后续迭代中将是上一次迭代函数返回的结果。所以,假如数组的长度为n,如果传入初始值,迭代次数为n;否则为n-1。

比如实现数组 arr = [1,2,3,4] 求数组的和

let arr = [1,2,3,4];
arr.reduce(function(pre,cur){return pre + cur}); // return 10
复制代码

实际上reduce还有很多重要的用法,这是因为累加器的值可以不必为简单类型(如数字或字符串),它也可以是结构化类型(如数组或对象),这使得我们可以用它做一些其他有用的事情,比如:

  • 将数组转换为对象
  • 展开更大的数组
  • 在一次遍历中进行两次计算
  • 将映射和过滤函数组合
  • 按顺序运行异步函数

将数组转化为对象

在实际业务开发中,你可能遇到过这样的情况,后台接口返回的数组类型,你需要将它转化为一个根据id值作为key,将数组每项作为value的对象进行查找。

例如:

const userList = [{id: 1,username: 'john',sex: 1,email: 'john@163.com'},{id: 2,username: 'jerry',sex: 1,email: 'jerry@163.com'},{id: 3,username: 'nancy',sex: 0,email: ''}
];
复制代码

如果你用过lodash这个库,使用_.keyBy这个方法就能进行转换,但用reduce也能实现这样的需求。

function keyByUsernameReducer(acc, person) {return {...acc, [person.id]: person};
}
const userObj = userList.reduce(keyByUsernameReducer, {});
console.log(userObj);
// {
//     1: {
//         id: 1,
//         username: 'john',
//         sex: 1,
//         email: 'john@163.com'
//     },
//     2: {
//       id: 2,
//       username: 'jerry',
//       sex: 1,
//       email: 'jerry@163.com'
//     },
//     3: {
//       id: 3,
//       username: 'nancy',
//       sex: 0,
//       email: ''
//     }
// }
复制代码

将小数组展开成大数组

试想这样一个场景,我们将一堆纯文本行读入数组中,我们想用逗号分隔每一行,生成一个更大的数组名单。

const fileLines = ['Inspector Algar,Inspector Bardle,Mr. Barker,Inspector Barton','Inspector Baynes,Inspector Bradstreet,Inspector Sam Brown','Monsieur Dubugue,Birdy Edwards,Inspector Forbes,Inspector Forrester','Inspector Gregory,Inspector Tobias Gregson,Inspector Hill','Inspector Stanley Hopkins,Inspector Athelney Jones'
];function splitLineReducer(acc, line) {return acc.concat(line.split(/,/g));
}
const investigators = fileLines.reduce(splitLineReducer, []);
console.log(investigators);
// [
//   "Inspector Algar",
//   "Inspector Bardle",
//   "Mr. Barker",
//   "Inspector Barton",
//   "Inspector Baynes",
//   "Inspector Bradstreet",
//   "Inspector Sam Brown",
//   "Monsieur Dubugue",
//   "Birdy Edwards",
//   "Inspector Forbes",
//   "Inspector Forrester",
//   "Inspector Gregory",
//   "Inspector Tobias Gregson",
//   "Inspector Hill",
//   "Inspector Stanley Hopkins",
//   "Inspector Athelney Jones"
// ]
复制代码

我们从长度为5的数组开始,最后得到一个长度为16的数组。

另一种常见增加数组的情况是flatMap,有时候我们用map方法需要将二级数组展开,这时可以用reduce实现扁平化

例如:

Array.prototype.flatMap = function(f) {const reducer = (acc, item) => acc.concat(f(item));return this.reduce(reducer, []);
}const arr = ["今天天气不错", "", "早上好"]const arr1 = arr.map(s => s.split(""))
// [["今", "天", "天", "气", "不", "错"],[""],["早", "上", "好"]]const arr2 = arr.flatMap(s => s.split(''));
// ["今", "天", "天", "气", "不", "错", "", "早", "上", "好"]
复制代码

在一次遍历中进行两次计算

有时我们需要对数组进行两次计算。例如,我们可能想要计算数字列表的最大值和最小值。我们可以通过两次通过这样做:

const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
const maxReading = readings.reduce((x, y) => Math.max(x, y), Number.MIN_VALUE);
const minReading = readings.reduce((x, y) => Math.min(x, y), Number.MAX_VALUE);
console.log({minReading, maxReading});
// {minReading: 0.2, maxReading: 5.5}
复制代码

这需要遍历我们的数组两次。但是,有时我们可能不想这样做。因为.reduce()让我们返回我们想要的任何类型,我们不必返回数字。我们可以将两个值编码到一个对象中。然后我们可以在每次迭代时进行两次计算,并且只遍历数组一次:

const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
function minMaxReducer(acc, reading) {return {minReading: Math.min(acc.minReading, reading),maxReading: Math.max(acc.maxReading, reading),};
}
const initMinMax = {minReading: Number.MAX_VALUE,maxReading: Number.MIN_VALUE,
};
const minMax = readings.reduce(minMaxReducer, initMinMax);
console.log(minMax);
// {minReading: 0.2, maxReading: 5.5}
复制代码

将映射和过滤合并为一个过程

还是先前那个用户列表,我们希望找到没有电子邮件地址的人的用户名,返回它们用户名用逗号拼接的字符串。一种方法是使用三个单独的操作:

  • 获取过滤无电子邮件后的用户
  • 获取用户名列表
  • 拼接用户名

将它们放在一起可能看起来像这样:

function notEmptyEmail(x) {return !!x.email
}function notEmptyEmailUsername(a, b) {return a ? `${a},${b}` : b
}const userWithEmail = userList.filter(notEmptyEmail);
const usernameWithEmail = userWithEmail.map((user=> user.username)
const userWithEmailFormatStr = userWithEmail.reduce(notEmptyEmailUsername, '');console.log(userWithEmailFormatStr);
// 'john,jerry'
复制代码

现在,这段代码是完全可读的,对于小的样本数据不会有性能问题,但是如果我们有一个庞大的数组呢?如果我们修改我们的reducer回调,那么我们可以一次完成所有事情:

function notEmptyEmail(x) {return !!x.email
}function notEmptyEmailUsername(usernameAcc, person){return (notEmptyEmail(person))? (usernameAcc ? `${usernameAcc},${person.username}` : `${person.username}`) : usernameAcc;
}const userWithEmailFormatStr = userList.reduce(notEmptyEmailUsername, '');console.log(userWithEmailFormatStr);
// 'john,jerry'
复制代码

在这个版本中,我们只遍历一次数组,一般建议使用filtermap的组合,除非发现性能问题,才推荐使用reduce去做优化。

按顺序运行异步函数

我们可以做的另一件事.reduce()是按顺序运行promises(而不是并行)。如果您对API请求有速率限制,或者您需要将每个prmise的结果传递到下一个promise,reduce可以帮助到你。

举一个例子,假设我们想要为userList数组中的每个人获取消息。

function fetchMessages(username) {return fetch(`https://example.com/api/messages/${username}`).then(response => response.json());
}function getUsername(person) {return person.username;
}async function chainedFetchMessages(p, username) {// In this function, p is a promise. We wait for it to finish,// then run fetchMessages().const obj  = await p;const data = await fetchMessages(username);return { ...obj, [username]: data};
}const msgObj = userList.map(getUsername).reduce(chainedFetchMessages, Promise.resolve({})).then(console.log);
// {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
复制代码

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

请注意,在此我们传递Promise作为初始值Promise.resolve(),我们的第一个API调用将立即运行。

下面是不使用async语法糖的版本

function fetchMessages(username) {return fetch(`https://example.com/api/messages/${username}`).then(response => response.json());
}function getUsername(person) {return person.username;
}function chainedFetchMessages(p, username) {// In this function, p is a promise. We wait for it to finish,// then run fetchMessages().return p.then((obj)=>{return fetchMessages(username).then(data=>{return {...obj,[username]: data}})})
}const msgObj = peopleArr.map(getUsername).reduce(chainedFetchMessages, Promise.resolve({})).then(console.log);
// {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
复制代码

PS:更多前端资讯、技术干货,请关注公众号「前端新视界

转载于:https://juejin.im/post/5cfcaa7ae51d45109b01b161

JavaScript 中数组方法 reduce 的妙用之处相关推荐

  1. 数组的reduce的妙用之处

    在前端开发过程.或者面试过程中,别人问你数组常用的操作,你也许立刻马上回答for循环.forEach.for..of. map.some-reduce等方法.我相信前端开发的小伙伴,10个人中有8个对 ...

  2. es5中数组方法unshift、splice、reduce使用

    1 es5和es6的区别 https://www.cnblogs.com/sunshinezjb/p/9248533.html 2 原型对象与原型链暂放 3 JSON对象的方法: parse, str ...

  3. Javascript中数组的sort方法的分析

    定义和用法 JavaScript中sort() 方法用于对数组的元素进行排序, 返回一个元素已经进行了排序的 Array 对象 语法 arrayObject.sort(sortby) 参数 描述 so ...

  4. java中字符串和数组如何比较_[Java教程]javascript中数组和字符串的方法比较

    [Java教程]javascript中数组和字符串的方法比较 0 2016-07-19 23:00:05 ×目录[1]可索引 [2]转换 [3]拼接[4]创建[5]位置 前面的话 字符串和数组有很多的 ...

  5. js中ES6新增的数组方法reduce(),和数组去重,降维。

    js中ES6新增的数组方法reduce() js数组reduce()详解 基本概念: reduce()方法接收一个函数作为累加器,数组中的每一值(从左到右)开始缩减,最后成为一个值 reduce()为 ...

  6. 几个关于js数组方法reduce的经典片段

    以下是个人在工作中收藏总结的一些关于javascript数组方法reduce的相关代码片段,后续遇到其他使用这个函数的场景,将会陆续添加,这里作为备忘. javascript数组那么多方法,为什么我要 ...

  7. JavaScript中数组使用总结

    一. 数组的概念 1.1 什么是数组 数组是指的数据的有序列表. 数组中每个值称之为数组的一个元素. 数组中的每个元素都有一个位置,这个位置称之为索引(下标.index).数组的索引是从 0 开始的 ...

  8. [乐意黎原创] JavaScript中数组使用总结

    原文地址:http://blog.csdn.net/u012468376/article/details/53147098 一. 数组的概念 1.1 什么是数组 数组是指的数据的有序列表. 数组中每个 ...

  9. ECMAScript3中数组方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

最新文章

  1. 快速原型工具 原型可视化
  2. 时间android版官方版下载,时间块app安卓下载
  3. JsonBuilder初出茅庐
  4. Apache Camel 2.20发布–新增功能
  5. 使用unity开发游戏时如觉得游戏声音太吵,点Mute Audio
  6. devstack安装openstack
  7. OPPO R9s成为情侣最喜欢礼物,今年情人节就送它了!
  8. 信息发布系统 Jquery+MVC架构开发(3) 解决方案创建
  9. canvas简易人机五子棋
  10. MediaCreationTool20H1制作Win10启动盘
  11. HTML+CSS+JS做一个简易音乐播放器
  12. 零基础能不能学计算机专业,零基础能学计算机专业吗?
  13. 蓝牙核心规范(V5.2)3.2-深入详解之数据传输架构
  14. ABAP编程中对内表的定义,后面接一个OCCURS (n)是代表什么意思。
  15. Photoshop安装方法
  16. 通往财富自由之路详细笔记(16)
  17. DB2 license过期解决方案
  18. sql 练习查询和“01“号的同学学习的课程完全相同的其他同学的信息
  19. JavaEE之--登录页面(用户名、密码、验证码)
  20. 复数加减java_用java实现复数的加减乘除运算

热门文章

  1. Jmeter响应中中文乱码怎么解决?
  2. linunx 定位最耗资源的进程
  3. C语言程序设计现代方法1,2,3章
  4. 解决PhoneGap在Android手机上的全屏问题
  5. $0,$#,$@,$+特殊符号的含义,shell的传递参数
  6. Ubuntu开启允许root用户远程登录
  7. Spring AOP切入点与通知XML类型
  8. 与计算机病毒相关的小故事,我和计算机病毒的故事2000字论文
  9. java redis 多节点,Redis单机多节点集群部署,超简单
  10. 计算机用语优秀怎么算,计算机专业用语