Javascript 中的map/reduce

看到一個提問的回答巧妙地使用了map/reduce操做,很優雅,因此來學習和總結一下javascript自帶的map/reduce的使用技巧,文章不會講的很深,純當科普一下知識點,若有解釋的不正確的地方,歡迎指正。

1.map

1.1 方法概述

map() 方法經過對原數組中的每一個元素進行必定的操做(共同調用一個方法),返回一個新的數組。javascript

1.2 簡單例子

/**
 * 每一個數組元素乘以2輸出
 */
var arr1 = [1,2,3,4];
var arr2 = arr1.map(x => {
    return x * 2;
});
console.log(arr2);    // [2,4,6,8]


/**
 * 輸出'Hello World'每一個字符所對應的 ASCII 碼組成的數組:
 */
var a = [].map.call("Hello World", x => {
    return x.charCodeAt(0); 
})
console.log(a);   //[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

*注:java

  1. [ ].map.call() 能夠寫成 Array.prototype.map.call(); 不懂的能夠參看以前的一篇文章:nodejs中call和apply的學習
  2. map函數不改變數組自己:

    var arr = [1,2,3,4];
    arr.map(x => { return Math.sqrt(x) });
    console.log(arr); //[1,2,3,4] 而不是[1, 1.414..., 1.732..., 2]node

1.3 語法解釋

var new_array = old_array.map(callback, [thisArg]);
其中 callback是生成新數組元素的函數,使用三個參數:
  • currentValue: callback 的第一個參數,數組中正在處理的當前元素。
  • index: callback 的第二個參數,數組中正在處理的當前元素的索引。
  • array: callback 的第三個參數,map 方法被調用的數組。

thisArg是可選的。執行 callback 函數時 使用的this 值。
返回值new_array :一個新數組,每一個元素都是回調函數的結果。segmentfault

對於["1", "2", "3"].map(parseInt)語句會輸出什麼呢?你可能覺的會是[1, 2, 3],但實際的結果是 [1, NaN, NaN],一般使用parseInt時,只須要傳遞一個參數,但實際上,parseInt能夠有兩個參數,第二個參數是進制數,能夠經過語句"alert(parseInt.length)===2"來驗證。map方法在調用callback函數時,會給它傳遞三個參數:當前正在遍歷的元素, 元素索引, 原數組自己。第三個參數parseInt會忽視, 但第二個參數不會,也就是說,parseInt把傳過來的索引值當成進制數來使用.從而返回了NaN.數組

function returnInt(element){
    return parseInt(element,10);
}

["1", "2", "3"].map(returnInt);   // 返回[1,2,3]

2.reduce

2.1 方法概述

reduce() 方法對累加器和數組的每一個值 (從左到右)應用一個函數,以將其減小爲單個值。內部實現應該是遍歷元素,理論上能夠經過forEach方法實現其功能。app

2.2 簡單例子

reduce一個較爲經常使用的場景就是累加或累乘:函數

/**
 * 輸出數組元素的乘積:
 */
var arr = [1,2,3];

var reducer = function add(stepProduct, item) { 
    return stepProduct * item; 
};

var product = arr.reduce(reducer, 1);

console.log(product);     //6


/**
 * 輸出數組元素的乘積(簡寫形式):
 */
var product = [1,2,3].reduce((stepProduct, item) => {
    return stepProduct * item;
},1);
console.log(product);


/**
 * 輸出數組元素的乘積(call形式,營造複雜的逼格):
 */
var product = [].reduce.call([1,2,3], (stepProduct, item) => {
    return stepProduct * item;
},1);
console.log(product);

reduce用在數組降維(扁平化處理)的例子中:學習

var list1 = [[0, 1], [2, 3], [4, 5]];
var list2 = [0, [1, [2, [3, [4, [5, [6]]]]]]];

function flatten(arr){
    return arr.reduce((acc, val) => {
        return acc.concat(Array.isArray(val) ? flatten(val) : val)
    }, []);
};

console.log(flatten(list1));  // [0, 1, 2, 3, 4, 5]
console.log(flatten(list2));  // [0, 1, 2, 3, 4, 5, 6]

*注:this

  1. concat() 方法用於鏈接兩個或多個數組。用法參見:JavaScript concat() 方法
  2. flatten用於數組降維。用法參見:JavaScript Array Flatten 與遞歸使用介紹_javascript技巧

回到文章一開始說起的一道題目,題目是這樣的:尋找字符串中出現次數最少的、而且首次出現位置最前的字符。如cbaacfdeaebb,符合要求的是f。其中一個用reduce來解決方案(姑且不評價是否最優方案):prototype

var str = [].reduce.call('cbaacfdeaebb', (p, n) => {
    return p[n] = (p[n] || 0) + 1,p;
},{});   // {c: 2, b: 3, a: 3, f: 1, d: 1, e: 2}

var s = Object.keys(str).reduce(function(p,n){
    return str[p] <= str[n] ? p : n;
});   //1

console.log(s,str[s]); 

/*
 * 如下是上面方法地球人比較容易看懂的改寫方式(寫完感受好多餘,蜜汁尷尬T_T):
 */
var str = 'cbaacfdeaebb'.split('').reduce((allItems, item) => {
    allItems[item] = (allItems[item] || 0) + 1;
    return allItems;
},{});   // {c: 2, b: 3, a: 3, f: 1, d: 1, e: 2}

*注:

return p[n] = (p[n] || 0) + 1,p; 是兩句代碼的簡寫形式:
p[n] = (p[n] || 0) + 1;
return p;

2.3 語法解釋

arr.reduce(callback, [initialValue])
其中 callback是執行數組中每一個值的函數,包含四個參數
  • accumulator: 上一次調用回調返回的值,或者是提供的初始值(initialValue)
  • currentValue: 數組中正在處理的元素
  • currentIndex: 數據中正在處理的元素索引,若是提供了 initialValue ,從0開始;不然從1開始
  • array: 調用 reduce 的數組

函數返回函數累計處理的結果。

initialValue爲可選項,其值用於第一次調用 callback 的第一個參數。

3 總結

map/reduce是ECMAScript5規範中出現的數組方法,在處理數組的時候巧妙地應用能夠達到意想不到的效果,推薦mozilla的技術文檔,系統介紹了各類函數的使用方法,其中map/reduce在Array目下。文中若有錯誤,歡迎批評指出。(ಡωಡ)

相關文章
相關標籤/搜索