iOS 谓词(NSPredicate)

谓词: 简单的说就是一个过滤器, 符合条件的留下, 不符合条件的删除

一. NSPredicate的基本语法

只要使用谓词(NSPredicate)都需要为谓词定义谓词表达式, 而这个表达式必须是一个返回BOOL的值
谓词表达式由表达式, 运算符和值构成

1: 比较运算符

(1)=、==:判断两个表达式是否相同
备注: 在谓词中=和==是都是相同的意思, 不是赋值

(2)>=, =>: 判断左边的表达式的值是否 大于或等于 右边表达式的值

(3)<=, =<: 判断左边的表达式的值是否 小于或等于 右边表达式的值

(4)>: 判断左边表达式的值是否 大于 右边表达式的值

(5)<: 判断左边表达式的值是否 小于 右边表达式的值

(6)!=, <>: 判断两个表达式是否 不相等

(7)BETWEEN: 该表达式必须满足: 表达式 BETWEEN {下限,上限} 的格式, 要求该表达式必须 大于或等于下限, 并小于等于上限

例: 
NSNumber *testNumber = @16;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF BETWEEN {12,19}"];
if ([predicate evaluateWithObject:testNumber]) {
     NSLog(@"testNumber = %@", testNumber);
}
else {
     NSLog(@"不符合");
}

2: 逻辑运算符:

(1)AND, &&: 逻辑与, 要求两个表达式的值都为YES时, 结果才为YES

例: NSArray *testArray = @[@1, @2, @3, @4, @5, @6];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF > 2 && SELF < 5"];
    NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];
    NSLog(@"filterArray: %@",filterArray);

输出结果:filterArray: (
    3,
    4
)

(2)OR, ||: 逻辑或, 要求其中一个表达式为YES时, 结果就是YES

例: NSArray *testArray = @[@1, @2, @3, @4, @5, @6];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF < 2 || SELF > 5"];
    NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];
    NSLog(@"filterArray: %@",filterArray);

输出结果:filterArray: (
    1,
    6
)

(3)NOT, !: 逻辑非, 对原有的表达式取反

例: NSNumber *testNumber = @1;
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF != 1"];
    if ([predicate evaluateWithObject:testNumber]) {
        NSLog(@"testNumber = %@", testNumber);
    }
    else {
        NSLog(@"不符合条件");
    }

输出结果: 不符合条件

3: 字符串比较运算符:

(1)BEGINSWITH: 检查某个字符串是否已指定的字符串开头

例: NSString *testString = @"testString";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF BEGINSWITH 't'"];
    if ([predicate evaluateWithObject:testString]) {
        NSLog(@"testString = %@", testString);
    }
    else {
        NSLog(@"不符合条件");
    }
输出结果: testString = testString

(2)ENDSWITH:检查某个字符串是否以指定的字符串结尾

(3)CONTAINS: 检查某个字符串是否包含指定的字符串

例: NSString *testString = @"testString";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS 'ts'"];
    if ([predicate evaluateWithObject:testString]) {
        NSLog(@"testString = %@", testString);
    }
    else {
        NSLog(@"不符合条件");
    }

输出结果: 不符合条件
备注: ts要用单引号' ' 括住, 否则会崩溃, 切记;

(4) LIKE: 检查某个字符串是否匹配指定的字符串模板. 其之后可以跟 ? 代表一个字符 和 * 代表任意多个字符 两个通配符;
例: "testString LIKE '*tS*'": 表示如果testString的值中包含ac则返回YES, 此时和CONTAINS相同

NSString *testString = @"testString";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF LIKE '*tS*'"];
if ([predicate evaluateWithObject:testString]) {
     NSLog(@"testString = %@", testString);
 }
 else {
     NSLog(@"不符合条件");
 }

输出结果:testString = testString

例: "testString LIKE '?tS*", 表示testString的第2, 3个字符为ac时, 返回YES

NSString *testString = @"testString";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF LIKE '?tS*'"];
if ([predicate evaluateWithObject:testString]) {
    NSLog(@"testString = %@", testString);
}
else {
    NSLog(@"不符合条件");
}

输出结果: 不符合条件

(5)MATCHES: 检查某个字符串是否匹配指定的正则表达式。虽然正则表达式的执行效率是最低的, 但其功能是最强大的, 也是我们 最常用 的。

注: 字符串比较都是区分 大小写 和重音符号 的。如:café和cafe是不一样的,Cafe和cafe也是不一样的。如果希望字符串比较运算不区分大小写和重音符号, 请在这些运算符后使用 [c], [d]选项。其中[c] 是不区分大小写,[d]是不区分重音符号,其写在字符串比较运算符之后,比如: testString LIKE[cd] 'cafe', 那么不论testString是cafe,Cafe还是café上面的表达式都会返回YES。

4. 集合运算符

(1)ANY、SOME: 集合中任意一个元素满足条件,就返回YES。

NSArray *testArray = @[@"zhao",@"qian",@"sun"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY SELF BEGINSWITH 'su'"];
if ([predicate evaluateWithObject:testArray]) {
    NSLog(@"testArray = %@", testArray);
}
else {
    NSLog(@"不符合条件");
}

输出结果:testArray = (
    zhao,
    qian,
    sun
)

(2)ALL: 集合中所有元素都满足条件,才返回YES

NSArray *testArray = @[@"zhao",@"qian",@"sun"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL SELF BEGINSWITH 'su'"];
if ([predicate evaluateWithObject:testArray]) {
    NSLog(@"testArray = %@", testArray);
}
else {
    NSLog(@"不符合条件");
}

输出结果: 不符合条件

(3)NONE: 集合中没有任何元素满足条件就返回YES

NSArray *testArray = @[@4, @5, @6, @7];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE SELF < 3"];
if ([predicate evaluateWithObject:testArray]) {
    NSLog(@"testArray = %@", testArray);
}
else {
    NSLog(@"不符合条件");
}

输出结果:testArray = (
    4,
    5,
    6,
    7
)

(4)IN: 等价于SQL语句中的IN运算符, 只有当左边表达式或值出现在右边的集合中才会返回YES

NSArray *filterArray = @[@"ab", @"abc"];
NSArray *testArray = @[@"a", @"ab", @"abc", @"abcd"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];
NSLog(@"%@",[testArray filteredArrayUsingPredicate:predicate]);
    
输出结果:(
    a,
    abcd
)

上面那段代码的作用是将testArray中和filterArray中相同的元素去除

5.直接量

在谓词表达式中可以使用如下直接量:
(1)FALSE、NO:代表逻辑假
(2)TRUE、YES:代表逻辑真
(3)NULL、NIL:代表空值
(4)SELF:代表正在被判断的对象自身
(5)"string"或'string':代表字符串
(6)数组:和c中的写法相同, 如:{'one','two','three'}
(7)数值: 包括证书、小数和科学计数法表示的形式
(8)十六进制数: 0x开头的数字
(9)八进制: 0o开头的数字
(10)二进制:0b开头的数字

6.保留字

下列单词都是保留字(不论大小写)
AND、OR、IN、NOT、ALL、ANY、SOME、NONE、LIKE、CASEINSENSITIVE、CI、MATCHES、CONTAINS、BEGINSWITH、ENDSWITH、BETWEEN、NULL、NIL、SELF、TRUE、YES、FALSE、NO、FIRST、LAST、SIZE、ANYKEY、SUBQUERY、CAST、TRUEPREDICATE、FALSEPREDICATE

注:虽然大小写都可以,但是更推荐使用大写来表示这些保留字

二、谓词的用法

1.定义谓词

NSPredicate *predicate = [NSPredicate predicateWithFormat:@""];

例一:
定义模型:
ZLPersonModel.h

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, ZLPersonSex){
    ZLPersonSexMale = 0,
    ZLPersonSexFemale
};

@interface ZLPersonModel : NSObject

/** 姓名 */
@property (nonatomic, copy) NSString *name;
/** 年龄 */
@property (nonatomic, assign, readonly) NSUInteger age;
/** 性别 */
@property (nonatomic, assign, readonly) ZLPersonSex sex;

+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex;
 
@end

ZLPersonModel.m

#import "ZLPersonModel.h"

@implementation ZLPersonModel

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex {
    self = [super init];
    if (self) {
        _name = name;
        _age = age;
        _sex = sex;
    }
    return self;
}

+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex {
    return [[self alloc] initWithName:name age:age sex:sex];
}

@end

开始使用:

ZLPersonModel *sunnyzl = [ZLPersonModel personWithName:@"sunnyzl" age:29 sex:ZLPersonSexMale];
ZLPersonModel *jack = [ZLPersonModel personWithName:@"jack" age:22 sex:ZLPersonSexMale];
  
//1: 判断姓名是否是以s开头
NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"name LIKE 's*'"];
//输出结果: sunnyzl: 1, jack: 0
NSLog(@"sunnyzl: %d, jack: %d",[pred1 evaluateWithObject:sunnyzl], [pred1 evaluateWithObject:jack]);
   
//2: 判断年龄是否大于25
NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"age > 25"];
//输出结果: sunnyzl的年龄是否大于25: 1, jack的年龄是否大于25: 0
NSLog(@"sunnyzl的年龄是否大于25: %d, jack的年龄是否大于25: %d",[pred2 evaluateWithObject:sunnyzl], [pred2 evaluateWithObject:jack]);

2.使用谓词过滤集合(很重要)

其实 谓词 本身就代表了一个逻辑条件,计算谓词之后返回的结果永远为BOOL类型的值。而谓词最常用的功能就是对集合进行过滤。当程序使用谓词对集合元素进行过滤时,程序会自动遍历其元素,并根据集合元素来计算谓词的值,当这个集合中的元素计算谓词并返回YES时,这个元素才会被保留下来。请注意 程序会自动遍历其元素,它会将自动遍历过之后返回为YES的值重新组合成一个集合返回。

//NSArray提供了如下方法使用谓词来过滤集合
//根据指定的谓词过滤集合,并将符合条件的元素组成新的集合返回(应用于NSArray中)
//参数: 指定的谓词
//返回:符合谓词条件的元素组成的集合
- (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate;   

//NSMutableArray提供了如下方法使用谓词来过滤集合
//根据指定的谓词过滤集合,剔除集合中不符合条件的元素
//参数: 指定的谓词
- (void)filterUsingPredicate:(NSPredicate *)predicate;    

//NSSet提供了如下方法使用谓词来过滤集合
//作用同NSArray中的方法
- (NSSet<ObjectType> *)filteredSetUsingPredicate:(NSPredicate *)predicate;    

//NSMutableSet提供了如下方法使用谓词来过滤集合
//作用同NSMutableArray中的方法
- (void)filterUsingPredicate:(NSPredicate *)predicate API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0));   

备注: 通过上面的描述可以看出, 使用谓词过滤不可变集合和可变集合的区别是: 过滤不可变集合时, 会   返回符合条件  的集合元素组成的新集合; 过滤可变集合时, 没有返回值, 会   直接剔除不符合条件   的集合元素.

例一:

NSMutableArray *arrayM = [@[@20, @40, @50, @30, @60, @70] mutableCopy];
//过滤大于50的值
NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"SELF > 50"];
[arrayM filterUsingPredicate:pred1];
NSLog(@"arrayM = %@",arrayM);
    
NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],
                   [ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFemale],
                   [ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],
                   [ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];
//取出名字中包含'son'的元素
NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"name CONTAINS 'son'"];
NSArray *newArray = [array filteredArrayUsingPredicate:pred2];
NSLog(@"newArray = %@",newArray);

输出结果:arrayM = (
    60,
    70
)

newArray = (
   "[name = Jackson, age = 30, sex = ZLPersonSexMale]",
   "[name = Johnson, age = 35, sex = ZLPersonSexMale]"
)

3. 在谓词中使用占位符参数

我们上面所有的例子中 谓词 总是 固定的, 然而我们在现实中处理变量时决定了谓词应该是 可变的. 下面我们来看看如何让谓词变化起来:
首先: 如果我们想在谓词表达式中使用变量,那么我们需要了解下列两种占位符:
%K: 用于动态传入属性名
%@: 用于动态设置属性值
除此之外, 还可以在谓词表达式中使用动态改变的属性值, 就像环境变量一样:

NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF CONTAINS $VALUE"];

上面表达式中, $VALUE是一个可以动态变化的值, 它最后其实是在字典中的一个key值, 所以可以根据你的需要写入不同的值, 但是必须有, 然后随着程序改变$VALUE这个 谓词 表达式的比较条件就可以动态改变.
例一:

NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],
                       [ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFemale],
                       [ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],
                       [ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];
//定义一个property来存放属性名, 定义一个value来存放值
NSString *property = @"name";
NSString *value = @"Jack";
//该谓词的作用是如果property属性含有值value时就取出放入新的数组内, 这里是name包含Jack
NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", property, value];
NSArray *newArray = [array filteredArrayUsingPredicate:pred];
NSLog(@"newArray = %@",newArray);

//创建谓词, 属性名改为age, 要求这个age包含$VALUE字符串
NSPredicate *predTemp = [NSPredicate predicateWithFormat:@"%K > $VALUE", @"age"];
//指定$VALUE的值为25
NSPredicate *pred1 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE":@25}];
NSArray *newArray1 = [array filteredArrayUsingPredicate:pred1];
NSLog(@"newArray1 = %@",newArray1);

//修改 $VALUE的值为32
NSPredicate *pred2 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE":@32}];
NSArray *newArray2 = [array filteredArrayUsingPredicate:pred2];
NSLog(@"newArray2 = %@",newArray2);


备注: 
//用常数值代替变量
//参数: 替换变量的字典
//返回: 替换变量后的谓词表达式
- (instancetype)predicateWithSubstitutionVariables:(NSDictionary<NSString *, id> *)variables;   

输出结果为:

newArray = (
         "[name = Jack, age = 20, sex = ZLPersonSexMale]",
         "[name = Jackson age = 30, sex = ZLPersonSexMale]"
     )

newArray1 = (
     "[name = Jackson, age = 30, sex = ZLPersonSexMale]",
     "[name = Johnson, age = 35, sex = ZLPersonSexMale]"
     )

newArray2 = (
     "[name = Johnson, age = 35, sex = ZLPersonSexMale]"
     )

原文:https://www.jianshu.com/p/88be28860cde

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342