来讲一讲如何理解 Javascript 中 Array 的 map/filter 函数 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
dannnney
V2EX    Javascript

来讲一讲如何理解 Javascript 中 Array 的 map/filter 函数

  •  
  •   dannnney 2017-02-07 22:09:56 +08:00 1724 次点击
    这是一个创建于 3173 天前的主题,其中的信息可能已经有所发展或是发生改变。

    打算每周写一篇文章,这是本周的文章,发出来大家一起分享一下。原文地址:

    http://www.jianshu.com/p/c4a77e5b8313

    前两周的文章:

    《什么是 MVVM 模式?》, http://www.jianshu.com/p/a898ef83f38c

    《做 Javascript 开发的同学,快来加入 TypeScript 大家庭吧!》, http://www.jianshu.com/p/f035cb4b9a40


    最近一直想写一系列文章,好好介绍一下 Javascript 下的函数编程,以及 ReactiveX 这个神器。拖延症犯了,连提纲都没规划出来。不过真心想聊这个话题,今天就先聊一聊一些简单的小技巧。

    如何理解 Array 的 map 函数 ?

    我们先来看一个简单的例子,假设有一个数组 [1,2,3,4],如果想对里面所有的数全部加上 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); 

    再来看另外一个稍微复杂一点的例子,假如我们从网路读取到一批数据,然后把他们渲染到页面上:

    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); 

    我们来观察一下这两个例子有什么样的共性?他们都是从一个数组中循环读出每一个数据,然后通过计算后得到一个新数据,然后放入到一个数组中,那我们来对代码进行一些优化:

    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); 

    我们把这两个代码片段中的循环逻辑修改成了一个小函数,大家再对比一下这两个 for 循环内部,几乎只有那个“小函数”的名字不一样,其他部分一模一样,我们给这个“小函数”一个好听的名字算法。

    对,我们在对一个数组进行循环处理时,大部分情况下,如果他们的输入是一个合法的数组,输出也是一个合法的数组,唯一不同的只是他们的“算法”不一样而已。于是,我们再来优化一下代码:

    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); 

    我们把一个 for 循环实现成了一个 map 函数,它接受两个参数:一个是数组,一个是算法函数。于是在我们下面的调用中,就再也看不到 for 循环的代码了。

    目前的 map 函数接受两个参数,跟我们最开始提到的 Array 对象的 map 函数还稍微有一点点差异,我们再调整一次:

    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; } } 

    我们把一个普通的 map 函数放到 Array 类上,那么它也只需要接受一个参数了,就是“算法”。所以我们的逻辑代码也相应调整为:

    [1,2,3,4].map(function(item) { return item + 10; }); getJsonFromRemote().map(function(item) { return '<li>' + item[i] + '</li>'; }); 

    如何理解 Array 的 filter 函数 ?

    如果 for 循环以后都变成了 map 函数调用的话,那么大家一定会有一个疑问:如果并不是想对一个数组进行“算法变化”,而只是想获取原数组中“符合规格”的那部分数据,该怎么做呢?

    这是就该 filter 出场了,看名字就可以知道,它就是一个过滤器。假设有一个 number 数组,我们只想获取它的偶数。我们先还是来看看传统的“笨办法”:

    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); 

    然后我们在用学习 map 时的办法,来对他进行一下优化:

    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 函数和 map 函数的实现很相似,主体依然是 for 循环,不过循环体的内部是先调用算法计算数据,然后依据计算结果来选择是否把当前数据复制到 output 中,结果为 true 则保留,为 false 则过滤掉。 even 函数的作用就是对传入的数据进行判断,如果是偶数,则返回 true 。最终的版本如下:

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

    综合范例

    我们接下来来实现一个非常真实的业务逻辑:我们用 nodejs 读取一个文件夹下所有的第一层子文件夹的名字,然后转化为绝对路径(以下代码暂时不考虑错误逻辑)。

    var fs = require('fs'); var path = require('path'); var dirPath = '...'; var cOntentList= fs.readdirSync(dirPath); //读取文件夹下的所有内容 var output = []; for(var i = 0; i < contentList.length; i++) { var name = contentList[i]; var subDirPath = path.resolve(dirPath, name); // 获取绝对路径 if(fs.statSync(subDirPath).isDirectory()) { // 判断是否为文件夹,排除掉文件 output.push(fullPath); } } 

    再来看看优化实现:

    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); 

    最后这段代码有人能看明白么?卖个关子,下次再讲。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2726 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 14:55 PVG 22:55 LAX 07:55 JFK 10:55
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86