JS reduce()方法詳解,使用reduce數組去重


 壹 ❀ 引

稍微有了解JavaScript數組API的同學,對於reduce方法至少有過一面之緣,也許是for與forEach太強大,或者filter,find很實用,在實際開發中我至始至終沒使用過reduce方法一次。很巧的是今天再刷面試題的過程中,遇到了一題關於數組操作的的題,相關解析中有人使用到了reduce方法,好吧我承認我看着有點茫然,因為我從未正眼過它,那么今天就給彼此一首歌的時間,讓我們好好了解你,關於reduce本文開始。

 貳 ❀ 關於reduce

一個完整的reduce方法應該是這樣:

array.reduce(function(accumulator, currentValue, currentIndex, array), initialValue)

它由一個回調函數與一個初始值組成,其中回調函數接受四個參數,我們先解釋回調函數形參與initialValue的含義:

1.initialValue [ɪˈnɪʃl]   [ˈvæljuː] 初始值

initialValue 表示reduce方法第一次執行時的初始值,是一個可選值。

2.accumulator [əˈkjuːmjəleɪtər] 累加器;積聚者

accumulator 正如翻譯的那樣,它是 reduce 方法多次執行的累積結果,accumulator 的初始值分兩種情況:

若有提供 initialValue 初始值,第一次循環時 accumulator 的值便為 initialValue,后續循環時 accumulator 為上次循環的返回值。

若未提供initialValue,第一次循環時 accumulator 的值為數組第一項arr[0],后續循環時為上次循環的返回值。

3.currentValue [ˈkɜːrənt] [ˈvæljuː] 當前值

這個應該不難理解,數組循環當前處理的值。currentValue 的初始值也受到initialValue的影響:

若有提供 initialValue 初始值,第一次循環currentValue 的值為數組第一項arr[0],后續變化隨索引遞增變化。

若未提供initialValue,第一次循環由於arr[0]成了accumulator 的值,所以currentValue 只能從arr[1]開始,后續變化隨索引遞增。

4.currentIndex

數組循環當前處理值的索引,currentValue 與 currentIndex是同步變化的。

5.array

當前正在被循環的數組。

說的有點糊塗?沒關系,我們通過例子循序漸進的來理解,加深這幾個參數的印象。先看第一個例子:

let arr = ['e', 'l', 'l', 'o'];
arr.reduce((accumulator, currentValue, currentIndex, array) => {
    console.log(accumulator, currentValue, currentIndex, array);
});

我們直接打印出四個參數,咦?為什么 accumulator第一次循環是e,后續循環怎么都是undefined了?

前面說了,由於reduce方法沒有提供初始值,所以第一次循環數組的第一項作為了reduce方法的初始值,后續循環中由於沒 return操作,導致accumulator拿不到上次返回值,所以就是undefined了。

我們在console后面加上return操作,再看:

return accumulator+currentValue;

這不就有值了嗎,所以使用reduce方法得記住,由於reduce是一個對數組累積操作的方法,在使用中一定得記得加return返回你希望累積操作的數據。

那也不對啊,我數組明明有四項,照輸出來看reduce方法怎么只執行了三次?

這是因為我們沒提供初始值 initialValue ,導致reduce方法將數組的第一項作為了初始值,所以循環第一次是從數組第二項開始的,我們嘗試給reduce添加一個默認值:

let arr = ['e', 'l', 'l', 'o'];
arr.reduce((accumulator, currentValue, currentIndex, array) => {
    console.log(accumulator, currentValue, currentIndex, array);
    return accumulator+currentValue;
},'h');

可以看到有了默認值,數組第一次循環是從索引 0 開始,完完整整的執行了四次。

也不對啊,accumulator不是循環的累加值嗎,執行完畢了怎么顯示的是 hell,o怎么沒加進去?這不是因為我console寫在了return前面了么(我故意的),執行完最后一次跳出循環,reduce方法會返回最終的執行結果,我們用一個變量來保存試試,像這樣:

let arr = ['e', 'l', 'l', 'o'];
let result = arr.reduce((accumulator, currentValue, currentIndex, array) => {
    return accumulator+currentValue;
},'h');
console.log(result);//hello

看,這不就是一個完整的單詞 hello了;

其實對於reduce讓人疑惑的無非就是initialValue與accumulator,currentValue的關系,這里我們做個小總結:

如果reduce有提供初始值,則循環從索引0開始,此時accumulator就是initialValue,currentValue值就是arr[0];如果reduce未提供初始值,則arr[0]作為初始值賦予給accumulator,循環從索引1開始,currentValue值就是arr[1]了;

 叄 ❀ reduce作用

那么到這里我們詳細介紹了reduce方法的參數與執行規則,了解了這些,我們可以用reduce方法做些什么呢?

1.數組求和

reduce方法本意就是用來記錄循環的累積結果,用於數組求和是最合適不過了。比如我們要求數組 [1,2,3,4] 的元素之和,用forEach你得這樣寫:

let total = 0;
[1, 2, 3, 4].forEach(item => total += item);
console.log(total); //10

但通過reduce方法就簡單的多,我們可以這么寫:

let total = [1, 2, 3, 4].reduce((accumulator, current) => accumulator += current); // 10

假設我們希望求數字90與數組 [ 1,2,3,4] 元素的和呢,那就這么寫:

let total = [1, 2, 3, 4].reduce((accumulator, current) => accumulator += current, 90); // 100

2.數組去重

比如我們要將數組 [1,2,2,4,null,null] 去除掉重復項,用filter可以這樣做:

let arr = [1, 2, 2, 4, null, null].filter((item, index, arr) => arr.indexOf(item) === index); // [1,2,4,null]

當然單說實現使用 new Set 更簡單:

let arr = [...new Set([1, 2, 2, 4, null, null])]; // [1,2,4,null]

現在我們知道了reduce方法,其實也可以通過reduce去重,像這樣:

let arr = [1, 2, 2, 4, null, null].reduce((accumulator, current) => {
    return accumulator.includes(current) ? accumulator : accumulator.concat(current);
}, []);

3.數組降維

比如我們要將二維數組 [[1,2],[3,4],[5,6]] 降維成一維數組,最簡單的做法是通過flat方法,像這樣:

let arr = [[1,2],[3,4],[5,6]].flat();//[1, 2, 3, 4, 5, 6]

通過reduce也挺簡單,我們可以結合concat方法,像這樣:

let arr = [[1,2],[3,4],[5,6]].reduce((accumulator, current)=>accumulator.concat(current),[]);//[1, 2, 3, 4, 5, 6]

那如果是個多維數組呢,reduce可以這樣做:

let arr = [0,[1],[2, 3],[4, [5, 6, 7]]];

let dimensionReduction = function (arr) {
    return arr.reduce((accumulator, current) => {
        return accumulator.concat(
            Array.isArray(current) ? 
            dimensionReduction(current) : 
            current
            );
    }, []);
}
dimensionReduction(arr); //[0, 1, 2, 3, 4, 5, 6, 7]

相對而言,多維數組降維flat會更簡單,當然flat存在兼容問題:

let arr = [0,[1],[2, 3],[4, [5, 6, 7]]].flat(Infinity);// [0, 1, 2, 3, 4, 5, 6, 7]

 肆 ❀ 使用注意

在使用reduce方法有一點需要注意,若有初始值但數組為空,或無初始值但數組只有一項時,reduce方法都不會執行。

[].reduce(() => console.log(1), 1); //不會執行
[1].reduce(() => console.log(1)); //不執行

若數組為空且沒有初始值,reduce方法報錯。

[].reduce(() => console.log(1)); //報錯

所以如果沒有初始值,你至少得保證數組有2項才能執行;如果給了初始值,你至少得保證數組有一項才能執行。

[1, 2].reduce(() => console.log(1)); //1
[1].reduce(() => console.log(1), 1); //1

 伍 ❀ 總

通過文本,我們知道了reduce是一個進行累積操作的方法,當我們提供初始值時,循環從0開始,如果不提供,則循環從索引1開始。

我們知道了reduce還有那么點傲嬌,如果數組為空且不提供初始值時reduce會報錯,如果想reduce執行,你的數組最低標准應該有一項,同時提供默認值(或數組有兩項無初始值)。

那么到這里,關於reduce方法參數與基本用法就全部介紹完畢了。

 參考

JS數組reduce()方法詳解及高級技巧

MDN


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM