如何理解 JavaScript 中 Array 的 map/filter 函數(shù)

最近一直想寫一系列文章具钥,好好介紹一下Javascript下的函數(shù)編程污它,以及 ReactiveX 這個(gè)神器侦锯。拖延癥犯了驼鞭,連提綱都沒規(guī)劃出來。不過真心想聊這個(gè)話題尺碰,今天就先聊一聊一些簡單的小技巧挣棕。

如何理解 Array 的 map 函數(shù) 译隘?

我們先來看一個(gè)簡單的例子,假設(shè)有一個(gè)數(shù)組 [1,2,3,4]洛心,如果想對(duì)里面所有的數(shù)全部加上10固耘,那么最普通的做法就是:

var input = [1,2,3,4];
var output = [];
for(var i = 0; i < input.length; i++ ) {
    var newData = input[i] + 10 ;
    output.push(newData);
}
console.log(output);

再來看另外一個(gè)稍微復(fù)雜一點(diǎn)的例子,假如我們從網(wǎng)路讀取到一批數(shù)據(jù)词身,然后把他們渲染到頁面上:

var dataList = getJsonFromRemote();
var output = [];
for(var i = 0; i < dataList.length; i++ ) {
    var el = '<li>' + dataList[i] + '</li>'
    output.push(el);
}
console.log(output);

我們來觀察一下這兩個(gè)例子有什么樣的共性厅目?他們都是從一個(gè)數(shù)組中循環(huán)讀出每一個(gè)數(shù)據(jù),然后通過計(jì)算后得到一個(gè)新數(shù)據(jù)法严,然后放入到一個(gè)數(shù)組中损敷,那我們來對(duì)代碼進(jìn)行一些優(yōu)化:

function add(data) {
    return data + 10;
}

var input = [1,2,3,4];
var output = [];
for(var i = 0; i < input.length; i++ ) {
    var newData = add(input[i]);
    output.push(newData);
}
console.log(output);
function renderLi(data) {
    return '<li>' + dataList[i] + '</li>';
}

var input = getJsonFromRemote();
var output = [];
for(var i = 0; i < input.length; i++ ) {
    var newData = renderLi(input[i]);
    output.push(el);
}
console.log(output);

我們把這兩個(gè)代碼片段中的循環(huán)邏輯修改成了一個(gè)小函數(shù),大家再對(duì)比一下這兩個(gè)for循環(huán)內(nèi)部深啤,幾乎只有那個(gè)“小函數(shù)”的名字不一樣拗馒,其他部分一模一樣,我們給這個(gè)“小函數(shù)”一個(gè)好聽的名字——算法溯街。

對(duì)诱桂,我們?cè)趯?duì)一個(gè)數(shù)組進(jìn)行循環(huán)處理時(shí),大部分情況下呈昔,如果他們的輸入是一個(gè)合法的數(shù)組挥等,輸出也是一個(gè)合法的數(shù)組,唯一不同的只是他們的“算法”不一樣而已韩肝。于是触菜,我們?cè)賮韮?yōu)化一下代碼:

function map(array, func) {
    var output = [];
    for(var i = 0; i < array.length; i++ ) {
        output.push(func(array[i]));
    }
    return output;
}

function add(data) {
    return data + 10;
}

function renderLi(data) {
    return '<li>' + data[i] + '</li>';
}

var input1 = [1,2,3,4];
var output1 = map(input1, add);

var input2 = getJsonFromRemote();
var output2 = map(input2, renderLi);

我們把一個(gè) for 循環(huán)實(shí)現(xiàn)成了一個(gè) map 函數(shù)九榔,它接受兩個(gè)參數(shù):一個(gè)是數(shù)組哀峻,一個(gè)是算法函數(shù)。于是在我們下面的調(diào)用中哲泊,就再也看不到 for 循環(huán)的代碼了剩蟀。

目前的 map 函數(shù)接受兩個(gè)參數(shù),跟我們最開始提到的 Array 對(duì)象的 map 函數(shù)還稍微有一點(diǎn)點(diǎn)差異切威,我們?cè)僬{(diào)整一次:

class Array {
    constructor() {
        this._list = [];
    }

    map(func) {
        var output = [];
        for(var i = 0; i < this._list.length; i++ ) {
            output.push(func(array[i]));
        }
        return output;
    }
}

我們把一個(gè)普通的 map 函數(shù)放到 Array 類上育特,那么它也只需要接受一個(gè)參數(shù)了,就是“算法”先朦。所以我們的邏輯代碼也相應(yīng)調(diào)整為:

[1,2,3,4].map(function(item) {
    return item + 10;
});

getJsonFromRemote().map(function(item) {
    return '<li>' + item[i] + '</li>';
});

如何理解 Array 的 filter 函數(shù) 缰冤?

如果 for 循環(huán)以后都變成了 map 函數(shù)調(diào)用的話,那么大家一定會(huì)有一個(gè)疑問:如果并不是想對(duì)一個(gè)數(shù)組進(jìn)行“算法變化”喳魏,而只是想獲取原數(shù)組中“符合規(guī)格”的那部分?jǐn)?shù)據(jù)棉浸,該怎么做呢?

這是就該 filter 出場了刺彩,看名字就可以知道迷郑,它就是一個(gè)過濾器枝恋。假設(shè)有一個(gè) number 數(shù)組,我們只想獲取它的偶數(shù)嗡害。我們先還是來看看傳統(tǒng)的“笨辦法”:

var input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var output = [];
for(var i = 0; i < input.length; i++ ) {
    if(input[i] % 2 == 0) {
        output.push(input[i]);
    }
}
console.log(output);

然后我們?cè)谟脤W(xué)習(xí) map 時(shí)的辦法焚碌,來對(duì)他進(jìn)行一下優(yōu)化:

function filter(input, func) {
    var output = [];
    for(var i = 0; i < input.length; i++ ) {
        if(func(input[i])) {
            output.push(input[i]);
        }
    }
    return output;
}

function even(data) {
    return input[i] % 2 == 0;
}

var input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var output = filter(input, even);
console.log(output);

可以看到 filter 函數(shù)和 map 函數(shù)的實(shí)現(xiàn)很相似,主體依然是 for 循環(huán)霸妹,不過循環(huán)體的內(nèi)部是先調(diào)用算法計(jì)算數(shù)據(jù)十电,然后依據(jù)計(jì)算結(jié)果來選擇是否把當(dāng)前數(shù)據(jù)復(fù)制到 output 中,結(jié)果為 true 則保留叹螟,為 false 則過濾掉摆出。even 函數(shù)的作用就是對(duì)傳入的數(shù)據(jù)進(jìn)行判斷,如果是偶數(shù)首妖,則返回 true偎漫。最終的版本如下:

[1, 2, 3, 4, 5, 6, 7, 8, 9].filter(function(item) {
    return item[i] % 2 == 0;
});

綜合范例

我們接下來來實(shí)現(xiàn)一個(gè)非常真實(shí)的業(yè)務(wù)邏輯:我們用 nodejs 讀取一個(gè)文件夾下所有的第一層子文件夾的名字,然后轉(zhuǎn)化為絕對(duì)路徑(以下代碼暫時(shí)不考慮錯(cuò)誤邏輯)有缆。

var fs = require('fs');
var path = require('path');

var dirPath = '...';
var contentList = fs.readdirSync(dirPath);  //讀取文件夾下的所有內(nèi)容
var output = [];
for(var i = 0; i < contentList.length; i++) {
    var name = contentList[i];
    var subDirPath = path.resolve(dirPath, name); // 獲取絕對(duì)路徑
    if(fs.statSync(subDirPath).isDirectory()) {  // 判斷是否為文件夾象踊,排除掉文件
        output.push(fullPath);
    }
}

再來看看優(yōu)化實(shí)現(xiàn):

var fs = require('fs');
var path = require('path');

var dirPath = '...';
var output = fs.readdirSync(dirPath).map(function(item){
    return path.resolve(dirPath, item);
}).filter(function(item) {
    return fs.statSync(item).isDirectory();
});

再換成 lambda 版本看看?

var fs = require('fs');
var path = require('path');

function getSubDirList(dirPath) {
    return fs.readdirSync(dirPath)
        .map(item => path.resolve(dirPath, item))
        .filter(item => fs.statSync(item).isDirectory());
}

var dirPath = '...';
var output = getSubDirList(dirPath);

怎么樣棚壁? 有沒有很清爽的感覺杯矩?不過我更喜歡下面這種美妙的寫法:

var R = require('ramda');
var fs = require('fs');
var path = require('path');

var getSubDirList = R.compose(
    R.filter(item => fs.statSync(item).isDirectory()),
    R.map(item => path.resolve(dirPath, item)),
    fs.readdirSync
);

var dirPath = '...';
var output = getSubDirList(dirPath);

最后這段代碼有人能看明白么?賣個(gè)關(guān)子袖外,下次再講史隆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市曼验,隨后出現(xiàn)的幾起案子泌射,更是在濱河造成了極大的恐慌,老刑警劉巖鬓照,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熔酷,死亡現(xiàn)場離奇詭異,居然都是意外死亡豺裆,警方通過查閱死者的電腦和手機(jī)拒秘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臭猜,“玉大人躺酒,你說我怎么就攤上這事∶锔瑁” “怎么了羹应?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丐膝。 經(jīng)常有香客問我量愧,道長钾菊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任偎肃,我火速辦了婚禮煞烫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘累颂。我一直安慰自己滞详,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布紊馏。 她就那樣靜靜地躺著料饥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朱监。 梳的紋絲不亂的頭發(fā)上岸啡,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音赫编,去河邊找鬼巡蘸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛擂送,可吹牛的內(nèi)容都是我干的悦荒。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼嘹吨,長吁一口氣:“原來是場噩夢啊……” “哼搬味!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蟀拷,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤碰纬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后匹厘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘀趟,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年愈诚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牛隅。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炕柔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出媒佣,到底是詐尸還是另有隱情匕累,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布默伍,位于F島的核電站欢嘿,受9級(jí)特大地震影響衰琐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炼蹦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一羡宙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掐隐,春花似錦狗热、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至探颈,卻和暖如春熟丸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伪节。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工虑啤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人架馋。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓狞山,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叉寂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子萍启,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容