开发中经常会遇到从数组中进行筛选数据的情况. 筛选数据本身并不困难, 但是这里要讲的是如何使用更加简单的方法来进行筛选.
正文
假设情景
假设我们有一批用户数据, 包含 姓名, 年龄, 身高, 体重 信息.
首先创建一个 Person 类, 代码如下.
1
2
3
4
5
6
7
8
9
10
11
12
13
@interface Person : NSObject
@property (nonatomic, copy)   NSString *name;   // 姓名
@property (nonatomic, assign) NSInteger age;    // 年龄
@property (nonatomic, assign) CGFloat   height; // 身高
@property (nonatomic, assign) CGFloat   weight; // 体重
+ (instancetype)persionWithName:(NSString *)name
                            age:(NSInteger)age
                         height:(CGFloat)height
                         weight:(CGFloat)weight;
@end
数据对象数组
用户信息数据对象数组如下:
1
2
3
4
5
6
7
8
9
NSArray *persons =
@[[Person persionWithName:@"张三" age:16 height:178 weight:69],
  [Person persionWithName:@"李四" age:28 height:173 weight:66],
  [Person persionWithName:@"王五" age:21 height:159 weight:51],
  [Person persionWithName:@"赵六" age:18 height:165 weight:56],
  [Person persionWithName:@"孙七" age:21 height:185 weight:73],
  [Person persionWithName:@"张八" age:35 height:173 weight:61],
  [Person persionWithName:@"张九" age:19 height:165 weight:52],
  ];
需求
假设需求(1):
从用户数据中筛选出姓
张并且身高 > 170cm的人.
一般最简单的做法遍历筛选
1
2
3
4
5
6
7
8
NSMutableArray *result = [NSMutableArray new];
for (Person *p in persons) {
    // 1. 姓名以 `张` 为首
    // 2. 身高 > 170 cm.
    if ([p.name hasPrefix:@"张"] && p.height > 170) {
        [result addObject:p];
    }
}
这样 循环遍历数组 的做法也能完成我们的需求, 却不是本文主张的做法.
NSPredicate (谓词)
NSPredicate (谓词) 是一个用于定义逻辑条件 的类, 主要用于 检索 或 筛选. 对于每个做 iOS 开发的人来讲都不陌生, 但是他的强大功能却经常会被忽略掉.
同样的, 依旧
从用户数据中筛选出姓
张并且身高 > 170cm的人.NSPredicate从语法上来看, 跟SQL比较像. 如果从数据库筛选, 我们的SQL可能会这样写:
1
SELECT * FROM `Users` WHERE `name` like 张% AND `height` > 170;
NSPredicate 会这样写:
1
name like '张*' AND height > 180
如果在本题中, 代码是这样的:
1
2
3
4
5
6
NSPredicate *predicate =
// 定义逻辑
// 1. name 以 `张` 为首
// 2. height > 170
[NSPredicate predicateWithFormat:@"name like '张*' AND height > 180"];
NSArray *result = [persons filteredArrayUsingPredicate:predicate];
此处
*为通配符, 意为以 ‘张’ 为首, 结尾为0或多个字符. 无论张三还是张三丰都能被匹配成功.
筛选结果:
1
2
3
4
(
    "张三: 16岁  178cm  69kg",
    "张八: 35岁  173cm  61kg"
)
当然, 除了上面的筛选条件, 还有更多的玩法:
- 筛选出年龄介于 
20和30之间的用户:1
[NSPredicate predicateWithFormat:@"age > 20 AND age < 30"];
 - 筛选出姓 
王的用户:1 2 3 4
// 可以使用 'like' [NSPredicate predicateWithFormat:@"name like '王*'"]; // 也可以使用 'BEGINSWITH' [NSPredicate predicateWithFormat:@"name BEGINSWITH '王'"];
 - 筛选出姓名中包含 
三的用户:1
[NSPredicate predicateWithFormat:@"name like '*三*'"];
此处,
三表示三前后都可以有>=0字符. 比如:三,三毛,张三,张三丰,张三打太极拳,祖师张三丰都能够成功匹配.还有一种写法:
三表示三之前有1个字符, 后面有>=0个字符. 比如:张三,张三丰,张三打太极拳, 但是无法匹配祖师张三丰.