Javascript中数组要领reduce的妙用的地方

《Javascript中数组要领reduce的妙用的地方》

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 = peopleArr.reduce(keyByUsernameReducer, {});
console.log(userObj);

将小数组睁开成大数组

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

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.username}` : b.username
}

const userWithEmail = userList.filter(notEmptyEmail);
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:更多前端资讯、手艺干货,请关注民众号「前端新视界

《Javascript中数组要领reduce的妙用的地方》

    原文作者:hzrWeb
    原文地址: https://segmentfault.com/a/1190000019423048
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞