首页 iOS 数组常用操作: 筛选
文章
取消

iOS 数组常用操作: 筛选

开发中经常会遇到从数组中进行筛选数据的情况. 筛选数据本身并不困难, 但是这里要讲的是如何使用更加简单的方法来进行筛选.

正文

假设情景

假设我们有一批用户数据, 包含 姓名, 年龄, 身高, 体重 信息.

首先创建一个 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"
)

当然, 除了上面的筛选条件, 还有更多的玩法:

  1. 筛选出年龄介于 2030 之间的用户:
    1
    
    [NSPredicate predicateWithFormat:@"age > 20 AND age < 30"];
    
  2. 筛选出姓 的用户:
    1
    2
    3
    4
    
    // 可以使用 'like'
    [NSPredicate predicateWithFormat:@"name like '王*'"];
    // 也可以使用 'BEGINSWITH'
    [NSPredicate predicateWithFormat:@"name BEGINSWITH '王'"];
    
  3. 筛选出姓名中包含 的用户:
    1
    
    [NSPredicate predicateWithFormat:@"name like '*三*'"];
    

    此处, 表示 前后都可以有 >=0 字符. 比如: , 三毛, 张三, 张三丰, 张三打太极拳, 祖师张三丰 都能够成功匹配.

    还有一种写法: 表示 之前有 1 个字符, 后面有 >=0 个字符. 比如: 张三, 张三丰, 张三打太极拳, 但是无法匹配 祖师张三丰.

本文由作者按照 CC BY 4.0 进行授权