摘要:數組方法中,相比等常用的迭代方法,常常被我們所忽略,今天一起來探究一下在我們實戰開發當中,能有哪些妙用之處,下面從語法開始介紹。按順序運行異步函數我們可以做的另一件事是按順序運行而不是并行。函數返回一個對象,可以使用方法添加回調函數。
Javascript數組方法中,相比map、filter、forEach等常用的迭代方法,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"
在這個版本中,我們只遍歷一次數組,一般建議使用filter和map的組合,除非發現性能問題,才推薦使用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:更多前端資訊、技術干貨,請關注公眾號「前端新視界」
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104519.html
摘要:一個歸并方法第一個參數是每一項上調用的函數,該函數有四個參數累加回調返回值他是上一次調用時返回的累積值,或數組中正在處理的元素數組中正在處理的當前元素的索引。統計數組中重復出現項的個數,用對象表示。 前言 在實際js開發中對數組操作頻率非常高,看過一些小伙伴的一些用法,挺有意思,在這里小記(不全)一下,備忘。 showImg(https://segmentfault.com/img/r...
摘要:本書主要探索函數式編程的核心思想。我們在中應用的僅僅是一套基本的函數式編程概念的子集。我稱之為輕量級函數式編程。通常來說,關于函數式編程的書籍都熱衷于拓展閱讀者的知識面,并企圖覆蓋更多的知識點。,本書統稱為函數式編程者。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson - 《You-Dont-Know-JS》作者 譯者團隊(排名不分先后)...
摘要:我們在的第二個參數里面初始了回調函數第一個參數的類型和值,將字符串轉化為數組,那么迭代的結果將是一個對象,對象的每一項值就是字符串的字母。 因為用for循環被老大鄙視之后,這幾天都在偷偷摸摸的研究數組的那幾個迭代方法。使用下來,感覺確實妙用無窮,仿佛自己的逼格在無形中變得高大了一點點,哈哈,上一篇文章的簡單介紹確實有點糙,因此決定重新一些總結文章。 這篇文章就是專門總結reduce方法...
摘要:我們在的第二個參數里面初始了回調函數第一個參數的類型和值,將字符串轉化為數組,那么迭代的結果將是一個對象,對象的每一項值就是字符串的字母。 因為用for循環被老大鄙視之后,這幾天都在偷偷摸摸的研究數組的那幾個迭代方法。使用下來,感覺確實妙用無窮,仿佛自己的逼格在無形中變得高大了一點點,哈哈,上一篇文章的簡單介紹確實有點糙,因此決定重新一些總結文章。 這篇文章就是專門總結reduce方法...
閱讀 2609·2021-11-17 17:00
閱讀 1864·2021-10-11 10:57
閱讀 3716·2021-09-09 11:33
閱讀 911·2021-09-09 09:33
閱讀 3550·2019-08-30 14:20
閱讀 3312·2019-08-29 11:25
閱讀 2796·2019-08-26 13:48
閱讀 734·2019-08-26 11:52