Map/Reduce 之類的函數是 Javascript 裡面非常方便的函數,用的好可以讓你的程式碼更簡潔效能更高考試也考一百分。並且也可以學到 functional programming 的概念。
面對一個陣列裡的一堆資料,我們一定是從遍歷開始,一一處理裡面的每一筆資料。你也許已經非常熟悉如何遍歷陣列,最常見的不外乎就是兩種做法。
var myArr = [ 1, 2, 3 ];
for (var index in myArr) {
console.log(myArr[index]);
}
var myArr = [ 1, 2, 3 ];
myArr.forEach(function(element) {
console.log(element);
});
有些時候,我們想對每個陣列元素(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() 並不會修改到原本陣列的值。
若是我們得到一個包含許多數值的陣列,而我們想限定這些數值不得超過我們設定的上限值,這時我們可以這樣處理,來得到一個經過檢查校正過後的資料結果:
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);
處理陣列資料的工作中,其中一項最常見的就是數值加總,或是進行統計運算。同樣的,若你使用土法煉鋼的做法,大致上如下:
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 去存取外部的變數,程式碼不會到處去污染。
簡單來說,就是將一個複雜的陣列,扁平化成一維,這在很多資料處理或數值計算上相當有用。
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, 2 ] 相接起來後回傳
- 將被代入的 [ 1, 2 ] 與 [ 3, 4, 5 ] 相接起來後回傳
- 將被代入的 [ 1, 2, 3, 4, 5 ] 與 [ 6, 7, 8 ] 相接起來後回傳
我們也可以利用 .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);
以前面的例子來說,可以先對陣列資料進行校正和加工,然後對資料進行收斂和加總:
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);
ES6 已經上了實際的戰場,當 .map()/.reduce() 方法加上箭頭函數(Arrow Function ),然後又配合上 JavaScript 語言的特性,整個程式碼將變得更為簡短乾淨。
let newArr = [ 1, 2, 3, 4, 5 ].map((value) => value + 1);
Reference