这三个函数在编程语言中非常的实用,所以今天非常有必要跟大家探讨一下这三个函数的使用方法,我们拿iOS中的使用为例。
在正式介绍这三个函数之前,我们先提供一个资源:一个班级所有学生的一次考试成绩。
首先,我们先创建学生这个类型:
Student.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @interface Student : NSObject @property (nonatomic, strong) NSString * name; @property (nonatomic, assign) NSInteger math; @property (nonatomic, assign) NSInteger chinese; @property (nonatomic, assign) NSInteger english;
+ (instancetype)newStudentWithName:(NSString *)name math:(NSInteger)math chinese:(NSInteger)chinese english:(NSInteger)english; @end
@implementation Student
+ (instancetype)newStudentWithName:(NSString *)name math:(NSInteger)math chinese:(NSInteger)chinese english:(NSInteger)english { Student * student = [[Student alloc] init]; student.name = name; student.math = math; student.chinese = chinese; student.english = english; return student; }
|
1 2 3 4 5 6
| struct Student { let name: String let math: Int let chinese: Int let english: Int }
|
然后,我们创建一个班级的所有学生成绩信息(这里只提供5位同学):
1 2 3 4 5 6 7
| NSArray * students = @[ [Student newStudentWithName:@"Helen" math:79 chinese:98 english:89], [Student newStudentWithName:@"Jack" math:90 chinese:80 english:76], [Student newStudentWithName:@"Bomb" math:98 chinese:43 english:79], [Student newStudentWithName:@"Joke" math:56 chinese:87 english:30], [Student newStudentWithName:@"Marry" math:99 chinese:60 english:32] ];
|
1 2 3 4 5 6 7
| let students = [ Student(name: "Helen", math: 79, chinese: 98, english: 89), Student(name: "Jack", math: 90, chinese: 80, english: 76), Student(name: "Bomb", math: 98, chinese: 43, english: 79), Student(name: "Joke", math: 56, chinese: 87, english: 30), Student(name: "Marry", math: 99, chinese: 60, english: 32) ]
|
接下来,正式介绍这三个函数的作用:
Map
Map:一个数组到另一个数组的映射。
俗称将一个数组通过一定的运算法则转化为另一个数组,这两个数组里的元素个数是一样的,但是元素类型是可以不一样的。
[a] -> [b]
以上面的例子,要想取出所有同学的数学成绩,这个时候就可以考虑到map:
1 2 3
| let maths = students.map { (student) -> Int in return student.math }
|
其中students为原数组,maths为目标数组,map后面的大括号为转换法则。
这里用了Swift里的Closures。
遗憾的是,在Objective-C里,没有map,没有。。。
当然,我们也可以使用原始的方法for去遍历,也可以采用Objective-C里的enumerate系列方法,但是,当爱上了map,filter,reduce以后,我希望我直接调用的还是这几个函数,于是,我们来扩展NSArray类:
1 2 3 4 5 6
| @interface NSArray (Functional)
- (NSArray *)map:(id (^) (id obj))block;
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @implementation NSArray (Functional) - (NSArray *)map:(id (^) (id obj))block { NSMutableArray *array = [[NSMutableArray alloc] init]; [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { id element = block(obj); if (element) { [array addObject:element]; } }]; return array; }
@end
|
于是,还是上面的那个例子,在Objective-C中的实现为:
1 2 3
| NSArray * maths = [students map:^id(Student * obj) { return @(obj.math); }];
|
当然,上面的原数组是创建的一些假数据,如果你有一些真实的数据,也可以通过map来将数据源转化为Student模型。
Filter
Filter:过滤。
一个数组通过一定的运算法则过滤掉相应的元素,成为另一个数组,这两个数组里的元素个数是不一样的,但是元素类型是一样的。
[a] -> [a]
eg.我们用filter来取出所有语文成绩及格的同学:
1 2 3
| let passStudents = students.filter { (student) -> Bool in return student.chinese >= 60 }
|
同样的,students为原数组,passStudents为目标数组,filter后面的大括号为过滤法则。
在Objective-C中还是没有filter函数,我们还是以同样的方法来扩展NSArray:
在上面的NSArray+Founctional.h中添加:
1
| - (NSArray *)filter:(BOOL (^) (id obj))block;
|
在NSArray+Founctional.m中添加:
1 2 3 4 5 6 7 8 9 10
| - (NSArray *)filter:(BOOL (^) (id obj))block { NSMutableArray *array = [[NSMutableArray alloc] init]; [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { BOOL element = block(obj); if (element) { [array addObject:obj]; } }]; return array; }
|
上面的例子在oc中样式:
1 2 3
| NSArray * passStudents = [students filter:^BOOL(Student * obj) { return obj.chinese >= 60; }];
|
Reduce
Reduce:以一个数组为数据源,通过一定的运算法则,最后合成另一种类型,在有些编程语言中起名foldr.
[a] -> b
eg.我们用reduce来算出所有学生的英语成绩之和:
1 2 3
| let totalScore = students.reduce(0) { (sum, student) -> Int in return sum + student.english }
|
标:reduce后面的0为初始值,sum为每次循环之前算出的总和,student为本次循环的学生模型。
组合使用
当然,这三个函数也可以组合起来使用,来完成稍微复杂点的需求。
eg.我们要获取语文成绩及格的的所有学生姓名:
1 2 3 4 5
| students.filter { (student) -> Bool in return student.chinese >= 60 }.map { (student) -> String in return student.name }
|
eg.三门成绩都及格的所有同学的数学成绩之和:
1 2 3 4 5 6 7
| students.filter { (student) -> Bool in return student.math >= 60 && student.chinese >= 60 && student.english >= 60 }.reduce(0) { (sum, student) -> Int in return sum + student.english }
|