Skip to content

Latest commit

 

History

History
207 lines (156 loc) · 5.39 KB

05.md

File metadata and controls

207 lines (156 loc) · 5.39 KB

Javascript Map/Reduce 奇淫技巧

Map/Reduce 之類的函數是 Javascript 裡面非常方便的函數,用的好可以讓你的程式碼更簡潔效能更高考試也考一百分。並且也可以學到 functional programming 的概念。


走訪陣列

面對一個陣列裡的一堆資料,我們一定是從遍歷開始,一一處理裡面的每一筆資料。你也許已經非常熟悉如何遍歷陣列,最常見的不外乎就是兩種做法。

for loop

var myArr = [ 1, 2, 3 ];

for (var index in myArr) {
    console.log(myArr[index]);
}

forEach

var myArr = [ 1, 2, 3 ];

myArr.forEach(function(element) {
    console.log(element);
});

使用 .map() 對每個陣列元素加工

有些時候,我們想對每個陣列元素(Element)進行加工處理,於是最土法煉鋼的方法大概就是這樣:

var myArr = [ 1, 2, 3 ];

for (var index in myArr) {
    myArr[index] = myArr[index] + 1;
}

// [ 2, 3, 4 ]
console.log(myArr);

這時你可以使用 .map() 方法來達成同樣目的:

var myArr = [ 1, 2, 3 ];

var newArr = myArr.map(function(element) {
    return element + 1;
});

// [ 2, 3, 4 ]
console.log(newArr);

.map() 會將每一個元素代入處理函數,而處理函數回傳的值,會被收集組成一個新的陣列,這個新的陣列元素數量會和原本陣列的一樣。所以 .map() 並不會修改到原本陣列的值。


使用 .map() 進行資料校正處理

若是我們得到一個包含許多數值的陣列,而我們想限定這些數值不得超過我們設定的上限值,這時我們可以這樣處理,來得到一個經過檢查校正過後的資料結果:

var myArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var newArr = myArr.map(function(element) {
    // 數值大於五的數值視為五
    if (element > 5)
        return 5;
        
    return element;
});

// [ 1, 2, 3, 4, 5, 5, 5, 5, 5, 5 ]
console.log(newArr);

使用 .reduce() 進行數值加總

處理陣列資料的工作中,其中一項最常見的就是數值加總,或是進行統計運算。同樣的,若你使用土法煉鋼的做法,大致上如下:

var myArr = [ 1, 2, 3 ];
var result = 0;

for (var index in myArr) {
    result += myArr[index];
}

// 6
console.log(result);

若使用 .reduce(),可以這樣寫:

var myArr = [ 1, 2, 3 ];

// 處理每個元素後等待回傳結果,第一次處理時代入初始值 0
var result = myArr.reduce(function(prev, element) {
    // 與之前的數值加總,回傳後代入下一輪的處理
    return prev + element;
}, 0);

// 6
console.log(result);

改用 .reduce() 之後,陣列元素的加總計算,不會再一直存取到外部的 result 變數,而是算完結果後才將結果統計結果回傳。這樣做的好處,是不會再跨 Scope 去存取外部的變數,程式碼不會到處去污染。


利用 .reduce() 進行陣列扁平化

簡單來說,就是將一個複雜的陣列,扁平化成一維,這在很多資料處理或數值計算上相當有用。

var myArr = [
    [ 1, 2 ],
    [ 3, 4, 5 ],
    [ 6, 7, 8 ]
];

// 將所有元素都與之前代入的陣列相接起來,第一次處理時代入初始值空陣列
var newArr = myArr.reduce(function(arr, element) {
    // ex: [ 1, 2 ].concat([ 3, 4, 5 ])
    return arr.concat(element);
}, []);

// [ 1, 2, 3, 4, 5, 6, 7, 8 ]
console.log(newArr);

所以這個處理函數將會被執行三次:

  1. 將空陣列與 [ 1, 2 ] 相接起來後回傳
  2. 將被代入的 [ 1, 2 ] 與 [ 3, 4, 5 ] 相接起來後回傳
  3. 將被代入的 [ 1, 2, 3, 4, 5 ] 與 [ 6, 7, 8 ] 相接起來後回傳

利用 .reduce() 進行資料歸納和統計吧!

我們也可以利用 .reduce() 配合上物件操作,對陣列的內容進行統計工作:

var myArr = [
    'C/C++',
    'JavaScript',
    'Ruby',
    'Java',
    'Objective-C',
    'JavaScript',
    'PHP'
];

// 計算出每種語言出現過幾次
var langStatistics = myArr.reduce(function(langs, langName) {
    if (langs.hasOwnProperty(langName)) {
        langs[langName]++
    } else {
        langs[langName] = 1;
    }
    
    return langs;
}, {});

// { 'C/C++': 1, 'JavaScript': 2, 'Ruby': 1, 'Java': 1, 'Objective-C': 1, 'PHP': 1 }
console.log(langStatistics);

把 .map() 和 .reduce() 串接起來吧!

以前面的例子來說,可以先對陣列資料進行校正和加工,然後對資料進行收斂和加總:

var myArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var result = myArr
    .map(function(element) {
        // 數值大於五的數值視為五
        if (element > 5)
            return 5;
            
        return element;
    })
    .reduce(function(prev, element) {
        // 與之前的數值加總,回傳後代入下一輪的處理
        return prev + element;
    }, 0);

// 40
console.log(result);

結合 ECMAScript 6 後,世界都不一樣了

ES6 已經上了實際的戰場,當 .map()/.reduce() 方法加上箭頭函數(Arrow Function ),然後又配合上 JavaScript 語言的特性,整個程式碼將變得更為簡短乾淨。

let newArr = [ 1, 2, 3, 4, 5 ].map((value) => value + 1);

Reference

  1. http://fred-zone.blogspot.tw/2017/01/javascript-mapreduce.html

tags: Javascript map reduce