如何实现JavaScript的Map和Filter函数?
译者按: 鲁迅曾经说过,学习JavaScript最好方式莫过于敲代码了!
原文: Master Map & Filter, Javascript’s Most Powerful Array Functions
译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
这篇文章面向那些已经熟练使用for循环,但对Array.map和Array.filter并没有特别理解的开发者。本文将会手把手去实现这两个函数,来深入理解它们的工作原理。
Array.map
Array.map通过对输入的数组中每一个元素进行变换,返回由变换后的元素按序组成的新数组。原始数组的值不会被修改。假设我们相对一个数组中的每一个元素乘以3,使用for循环可以这样写。
for循环
|
var originalArr = [1, 2, 3, 4, 5];
var newArr = [];
for(var i = 0; i < originalArr.length; i++) {
newArr[i] = originalArr[i] * 3;
}
console.log(newArr); // -> [3, 6, 9, 12, 15]
|
接下来我们将这个for循环抽象成一个函数。
multiplyByThree函数
|
var originalArr = [1, 2, 3, 4, 5];
function multiplyByThree(arr) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = arr[i] * 3;
}
return newArr;
}
var arrTransformed = multiplyByThree(originalArr);
console.log(arrTransformed); // -> [3, 6, 9, 12, 15]
|
现在我们继续深化这个抽象思路,将multiplyByThree中对每一个元素乘以3部分抽象为一个新的函数。
|
var originalArr = [1, 2, 3, 4, 5];
function timesThree(item) {
return item * 3;
}
function multiplyByThree(arr) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = timesThree(arr[i]);
}
return newArr;
}
var arrTransformed = multiplyByThree(originalArr);
console.log(arrTransformed); // -> [3, 6, 9, 12, 15]
|
这样有什么好处呢?设想如果我们想对每一个元素乘以5,或则10,我们还要把整个for循环写一遍吗!
如果我们对timesThree函数稍作修改,就可以轻松的复用很多代码。
multiply函数
我们将:
|
function multiplyByThree(arr) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = timesThree(arr[i]);
}
return newArr;
}
|
重构为:
|
function multiply(arr, multiplyFunction) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = multiplyFunction(arr[i]);
}
return newArr;
}
|
我们将multiplyByThree重命名为multiply,并增加了一个参数。该参数是一个函数,定义了数组元素的变换规则。通过定义一个timesThree函数来达到实现对每一个数组元素乘以3的目的。
|
var originalArr = [1, 2, 3, 4, 5];
function timesThree(item) {
return item * 3;
}
var arrTimesThree = multiply(originalArr, timesThree);
console.log(arrTimesThree); // -> [3, 6, 9, 12, 15]
|
有何优点呢?我们可以很简单定义任何变换:
|
var originalArr = [1, 2, 3, 4, 5];
function timesFive(item) {
return item * 5;
}
var arrTimesFive = multiply(originalArr, timesFive);
console.log(arrTimesFive); // -> [5, 10, 15, 20, 25]
|
Map
我们进一步抽象:
|
function multiply(arr, multiplyFunction) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = multiplyFunction(arr[i]);
}
return newArr;
}
|
将multiply改为map, multiplyFunction改为transform:
|
function map(arr, transform) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = transform(arr[i]);
}
return newArr;
}
|
我们可以将任何对单个元素操作的函数传入map函数。比如,我们将所有字符都变换成大写:
|
function makeUpperCase(str) {
return str.toUpperCase();
}
var arr = ['abc', 'def', 'ghi'];
var ARR = map(arr, makeUpperCase);
console.log(ARR); // -> ['ABC', 'DEF, 'GHI']
|
Array.map
我们定义的map函数和原生的Array.map还是有区别的:数组不再需要作为第一个参数传入,而是在点(.)的左侧。如果使用我们定义的map函数,如下:
|
function func(item) {
return item * 3;
}
var arr = [1, 2, 3];
var newArr = map(arr, func);
console.log(newArr); // -> [3, 6, 9]
|
将其改写为使用Array.map函数的形式:
|
function func(item) {
return item * 3;
}
var arr = [1, 2, 3];
var newArr = arr.map(func);
console.log(newArr); // -> [3, 6, 9]
|
Arrary.map参数解析
除了变换函数外,Array.map还可以接收其它两个参数: 数组索引(index), 原始的数组。
|
function logItem(item) {
console.log(item);
}
function logAll(item, index, arr) {
console.log(item, index, arr);
}
var arr = ['abc', 'def', 'ghi'];
arr.map(logItem); // -> 'abc', 'def', 'ghi'
arr.map(logAll); // -> 'abc', 0, ['abc', 'def', 'ghi']
// -> 'def', 1, ['abc', 'def', 'ghi']
// -> 'ghi', 2, ['abc', 'def', 'ghi']
|
因此,你可以再变换函数中使用索引和原始的数组。比如:你想要将一个列表变为带序号的列表,则需要使用索引(index)参数:
|
function multiplyByIndex(item, index) {
return (index + 1) + '. ' + item;
}
var arr = ['bananas', 'tomatoes', 'pasta', 'protein shakes'];
var mappedArr = arr.map(multiplyByIndex);
console.log(mappedArr); // ->
// ["1. bananas", "2. tomatoes", "3. pasta", "4. protein shakes"]
|
因此,我们自己实现的map函数也应该支持这两个参数:
|
function map(arr, transform) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = transform(arr[i], i, arr);
}
return newArr;
}
|
当然,Array.map函数还有一些错误检查和执行优化的代码,我们定义的map只编码了核心功能。
Array.filter
Array.filter将数组中不满足条件的元素过滤,我们可以用for循环加上Array.push来实现。
for-loop
下面这段JS代码将所有大于5的元素筛选出来:
|
var arr = [2, 4, 6, 8, 10];
var filteredArr = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i] > 5) {
filteredArr.push(arr[i]);
}
}
console.log(filteredArr); // -> [6, 8, 10]
|
我们可以抽象这段代码,定义为一个函数:
|
function filterLessThanFive(arr) {
var filteredArr = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i] > 5){
filteredArr.push(arr[i]);
}
}
return filteredArr;
}
var arr1 = [2, 4, 6, 8, 10];
var arr1Filtered = filterLessThanFive(arr1);
console.log(arr1Filtered); // -> [6, 8, 10]
|
进一步抽象,将过滤条件抽出来:
|
function isGreaterThan5(item) {
return item > 5;
}
function filterLessThanFive(arr) {
var filteredArr = [];
for(var i = 0; i < arr.length; i++) {
if(isGreaterThan5(arr[i])) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
}
var originalArr = [2, 4, 6, 8, 10];
var newArr = filterLessThanFive(originalArr);
console.log(newArr); // -> [6, 8, 10]
|
将过滤条件函数作为参数传入:
|
function filterBelow(arr, greaterThan) {
var filteredArr = [];
for(var i = 0; i < arr.length; i++) {
if(greaterThan(arr[i])) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
}
var originalArr = [2, 4, 6, 8, 10];
|
大功告成!我们可以使用如下代码来取出所有大于5的元素:
|
function isGreaterThan5(item) {
return item > 5;
}
var newArr = filterBelow(originalArr, isGreaterThan5);
console.log(newArr); // -> [6, 8, 10];
|
Array.filter
我们将filterBelow重命名为filter, greaterThan重命名为testFunction:
|
function filter(arr, testFunction) {
var filteredArr = [];
for(var i = 0; i < arr.length; i++) {
if(testFunction(arr[i])) {
filteredArr.push(arr[i]);
}
}
return filteredArr;
}
|
这就是一个基本的Array.filter函数了!
|
var arr = ['abc', 'def', 'ghijkl', 'mnopuv'];
function longerThanThree(str) {
return str.length > 3;
}
var newArr1 = filter(arr, longerThanThree);
var newArr2 = arr.filter(longerThanThree);
console.log(newArr1); // -> ['ghijkl', 'mnopuv']
console.log(newArr2); // -> ['ghijkl', 'mnopuv']
|
同样,Array.filter也有索引(index)和原始数组这两个额外参数。
|
function func(item, index, arr) {
console.log(item, index, arr);
}
var arr = ['abc', 'def', 'ghi'];
arr.filter(func); // -> 'abc', 0, ['abc', 'def', 'ghi']
// -> 'def', 1, ['abc', 'def', 'ghi']
// -> 'ghi', 2, ['abc', 'def', 'ghi']
|
关于Fundebug:
Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了7亿+错误事件,得到了Google、360、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

版权声明:
转载时请注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2017/07/26/master_map_filter_by_hand_written/
如何实现JavaScript的Map和Filter函数?的更多相关文章
- JavaScript Array -->map()、filter()、reduce()、forEach()函数的使用
题目: 1.得到 3000 到 3500 之内工资的人. 2.增加一个年龄的字段,并且计算其年龄. 3.打印出每个人的所在城市 4.计算所有人的工资的总和. 测试数据: function getDat ...
- python 列表解析与map和filter函数
不知哪儿看到一个说法,大概是当map的函数参数可以直接引用一个已有的函数变量时(比如内建函数int,str之类的),用map更优美些,否则还是用列表解析更直观和快速. 我同意此说法. 昨天在写一个函数 ...
- jdk8-》stream⾥的map和filter函数使⽤
map函数 将流中的每⼀个元素 T(入参) 映射为 R(返回值)(类似类型转换) 类似遍历集合,对集合的每个对象做处理.场景:转换对象,如javaweb开发中集合⾥⾯的DO对象转换为DTO对象 ...
- javascript利用map,every,filter,some,reduce,sort对数组进行最优化处理
案例: var scoresTable=[ {id:11,name:"小张",score:80}, {id:22,name:"小王",score:95}, {i ...
- Python 有用的 map() deduce() filter() 函数
#!/usr/bin/python#5!+4!+3!+2!+1! #give 3 return 3*2*1def jiechen(n): N = map(lambda x:x+1,range(n)) ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- map/reduce/filter/lambda
Python内建了map()/reduce()/filter()函数. map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的It ...
- 第7条:用列表推导式来取代map和filter
核心知识点: 1.列表推导式要比内置的map和filter函数清晰,因为它无需额外编写lambda表达式. 2.列表推导式可以跳过输入列表中的某些元素,如果改用map来做,那就必须辅以filter方能 ...
随机推荐
- 盘点和反思在微信的阴影下艰难求生的移动端IM应用
本文原作者:李越,由银杏财经原创发布,本次内容改动. 1.前言 上线一周完成1.5亿元融资,上线10天总激活用户数超400万,8月29日单日新增用户超100万,这是子弹短信交出的最新成绩单(详见< ...
- 吴恩达机器学习笔记25-神经网络的模型表示2(Model Representation of Neural Network II)
( FORWARD PROPAGATION ) 相对于使用循环来编码,利用向量化的方法会使得计算更为简便.以上面的神经网络为例,试着计算第二层的值: 这只是针对训练集中一个训练实例所进行的计算.如果我 ...
- 第一节:ASP.NET开发环境配置
第一节:ASP.NET开发环境配置 什么是ASP.NET,学这个可以做什么,学习这些有什么内容? ASP.NET是微软公司推出的WEB开发技术. 2002年,推出第一个版本,先后推出ASP.NET2. ...
- dubbo集群容错解决方案
dubbo主要核心部件 Remoting:网络通信框架,实现了sync-over-async和request-response消息机制. RPC:一个远程过程调用的抽象,支持负载均衡.容灾和集群功能. ...
- eclipse如何使用log4j详解,你get了吗???
1.下载log4j jar包 log4j下载地址 http://logging.apache.org/log4j/2.x/download.html 2.log4j jar包引入项目 接下来 ...
- dpkg: 处理软件包 qjackctl (--configure)时出错解决方法
第一步:备份 $ sudo mv /var/lib/dpkg/info /var/lib/dpkg/info.bk 第二步:新建 $ sudo mkdir /var/lib/dpkg/info 第三步 ...
- rgba()和opacity之间的区别(面试题)
rgba()和opacity之间的区别: 相同点:rgba()和opacity都能实现透明效果: 不同点:opacity作用于元素,以及元素中所有的内容: rgba()只用于于元素的颜色,及背景色: ...
- Java 中初始化 List 集合的 6 种方式!
List 是 Java 开发中经常会使用的集合,你们知道有哪些方式可以初始化一个 List 吗?这其中不缺乏一些坑,今天栈长我给大家一一普及一下. 1.常规方式 List<String> ...
- 【学习笔记】分类算法-k近邻算法
k-近邻算法采用测量不同特征值之间的距离来进行分类. 优点:精度高.对异常值不敏感.无数据输入假定 缺点:计算复杂度高.空间复杂度高 使用数据范围:数值型和标称型 用例子来理解k-近邻算法 电影可以按 ...
- mysql 开发进阶篇系列 9 锁问题 (Innodb 行锁实现方式)
一.概述 Innodb 行锁是通过给索引上的索引项加锁来实现的.这一点与(oracle,sql server)不同后者是通过在数据块中对相应的数据行加锁.这意味着只有通过索引条件检索数据,innodb ...