• iOS之谓词(NSPredicate)(正则表达式和UIBarController)


    本文转发至:https://segmentfault.com/a/1190000000623005

    NSPredicate,这个类和我上一篇博文中提到的valueForKeyPath一样很强大。它的使用主要集中在两个方法中

    NSArray

    - (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate;
    

    NSMutableArray

    - (void)filterUsingPredicate:(NSPredicate *)predicate;
    

    还有NSSetNSMutableSet也可以用这个类筛选。

    下面我就来一一介绍这个类的用法,相信大家看完后会和我一样认为这个类真的很强大。

    筛选用法

    • 利用成员实例方法
      筛选出长度大于3的字符串
        NSArray *array = @[@"jim", @"cook", @"jobs", @"sdevm"];
        NSPredicate *pre = [NSPredicate predicateWithFormat:@"length > 3"];
        NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
    

    打印

    (
        cook,
        jobs,
        sdevm
    )
    

    lenght就是对数组成员执行[xxxx lenght]然后判断返回的NSUInteger值是否大于3。扩展到NSString其他方法比如integerValue

    NSArray *array = @[@"2", @"3", @"4", @"5"];
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"integerValue >= %@", @3];
    NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
    
    

    如果我不想用任何实例方法,想筛选成员本身应该怎么做呢。这时候就可以用self来代替

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"self CONTAINS %@", @3];
    

    CONTAINS用法后面会讲到

    再扩展到模型

    Test.h

    @interface Test : NSObject
    
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSNumber *code;
    
    @end
    
    
        Test *test1 = [[Test alloc]init];
        test1.name = @"西湖";
        test1.code = @1;
    
        Test *test2 = [[Test alloc]init];
        test2.name = @"西溪湿地";
        test2.code = @2;
    
        Test *test3 = [[Test alloc]init];
        test3.name = @"灵隐寺";
        test3.code = @3;
    
        NSArray *array = @[test1, test2, test3];
        NSPredicate *pre = [NSPredicate predicateWithFormat:@"code >= %@", @2];
        NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
    

    筛选出数组成员[test code]方法(code属性的get方法)返回值>=2的成员。这里的比较运算符同样也可以使用==!=<=<

    其实==不仅可以用来比较NSNumber对象,还可以用来判断NSString对象是否相同。

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name == %@", @"西湖"];
    

    筛选出name是"西湖"的对象数组。

    • ###NSString对象的操作
      前面提到==比较运算符可以起到- (BOOL)isEqualToString:(NSString *)aString;方法的效果,来判断字符串是否相同。那么字符串中包含某个字符串应该如何判断呢,在NSPredicate中可以用CONTAINS(大小写都可以)来表示包含关系。
     NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@", @"湖"];
    

    当判断的时候需要忽略大小写可以使用[cd]

    [c] 忽略大小写

    [d] 忽略重音符号

    [cd]既不区分大小写,也不区分发音符号。

    使用:

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", @"abc"];
    

    再涉及到一些更复杂的查询语句,比如判断字符串以某个字符串开头或者结尾,通配符的使用。

    BEGINSWITH(已某个字符串开头, begins with)

        NSString *targetString = @"h";
        NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",targetString];
    

    ENDSWITH(已某个字符串结尾, ends with)

        NSString *targetString = @"ing";
        NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",targetString];
    

    通配符 LIKE

    *代表一个或者多个或者是空

    ?代表一个字符

    Test *test1 = [[Test alloc]init];
    test1.name = @"absr";
    test1.code = @1;
    
    Test *test2 = [[Test alloc]init];
    test2.name = @"asb";
    test2.code = @2;
    
    Test *test3 = [[Test alloc]init];
    test3.name = @"raskj";
    test3.code = @3;
    
    NSArray *array = @[test1, test2, test3];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE %@", @"?b*"];
    NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
    

    结果是只有test1符合,like也可以接受[cd]

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE[cd] %@", @"?b*"];
    

    关系运算,包括了INBETWEENANDORNOT

    IN(之中)

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"code IN %@", @[@1, @3]];
    

    判断code是否@1或者是@2,也就是是否在数组中。

    OR(或,可以用||代替)

    OR可以用来代替IN达到同样的效果,但是OR更灵活。

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"code == %@ OR code == %@ ", @1, @3];
    

    效果和IN一样,但是OR可以判断不只一个属性

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"code == %@ OR name == %@ ", @1, @"asb"];
    

    BETWEEN(之间)

    通常用于判断NSNumber对象

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"code BETWEEN {1, 3}"];
    

    判断code是否>=1且<=3

    AND(且,可以用&&代替)

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"code >= %@ AND code <=%@", @1, @3];
    

    NOT(非,可以用!代替)

    NOT最常见的用法就是从一个数组中剔除另外一个数组的数据,可能有点绕,举个例子就很明朗了。

        NSArray *arrayFilter = @[@"abc1", @"abc2"];
        NSArray *arrayContent = @[@"a1", @"abc1", @"abc4", @"abc2"];
        NSPredicate *thePredicate = [NSPredicate predicateWithFormat:@"NOT (SELF in %@)", arrayFilter];
        NSLog(@"%@",[arrayContent filteredArrayUsingPredicate:thePredicate]);
    

    打印

    (
        a1,
        abc4
    )
    

    比起循环比较再加到新数组中,简单的不止一两点。

    前面提到的都是用+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;方法创建,还有另一种常用的方法:+ (NSPredicate*)predicateWithBlock:(BOOL (^)(id evaluatedObject, NSDictionary *bindings))block,用Block形式创建

    NSPredicate *pre = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
        Test *test = (Test *)evaluatedObject;
        if (test.code.integerValue > 2) {
            return YES;
        }
        else{
            return NO;
        }
    }];
    

    参数evaluatedObject表示数组成员,block必须返回YES或者NO,分别表示匹配还是不匹配。请忽略bindings参数,具体作用我也没搞清楚。

    多重筛选

    如果需要匹配数个属性的筛选,用AND或者OR来串联显然有点麻烦,NSCompoundPredicate类可以满足我们的需求,它可以将多个NSPredicate对象的组合,组合方式可以是AND或者OR

        NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"code >= %@", @3];
        NSPredicate *pre2 = [NSPredicate predicateWithFormat:@"code <= %@", @2];
    
        //以AND形式组合
        NSPredicate *pre = [NSCompoundPredicate andPredicateWithSubpredicates:@[pre1,pre2]];
        //以OR形式组合
        NSPredicate *pre = [NSCompoundPredicate orPredicateWithSubpredicates:@[pre1, pre2]];
    

    匹配用法

    其实NSPredicate不仅可以用于筛选,还可以用来判断匹配直接返回是否符合,主要方法是- (BOOL)evaluateWithObject:(id)object;,用法:

        Test *test1 = [[Test alloc]init];
        test1.name = @"absr";
        test1.code = @1;
    
        NSPredicate *pres = [NSPredicate predicateWithFormat:@"code == %@", @2];
        BOOL match = [pres evaluateWithObject:test1];
    

    当然最常用的还是配合配合正则表达式,列举几个常用的正则

    是否以a开头以e结尾

        NSString *string=@"assdbfe";
        NSString *targetString=@"^a.+e$";
        NSPredicate *pres = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", targetString];
        BOOL match = [pres evaluateWithObject:string];
    
    

    是否是邮箱

    NSString *strRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{1,5}"; 
    

    是否是手机号

    NSString *strRegex = @"[0-9]{1,20}";  
    

    不得不说,在利用+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;方法创建的NSPredicate对象的时候有个坑。

    在某些情况下,类似于上面例子中的code字符串不是很明确,创建的时候就会这样使用

    Test *test1 = [[Test alloc]init];
    test1.name = @"absr";
    test1.code = @1;
    
    Test *test2 = [[Test alloc]init];
    test2.name = @"asb";
    test2.code = @2;
    
    Test *test3 = [[Test alloc]init];
    test3.name = @"raskj";
    test3.code = @3;
    
    NSArray *array = @[test1, test2, test3];
    
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"%@ == %@", @"code", @2];
    NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
    

    注意NSPredicate对象的初始化方式。运行这块代码你会发现test2对象没有被查询出来。打印pre发现"code" == 2,这说明查找的是"code"方法的返回值,这显然行不通。

    如果查询的是属性,比如code是Test类的属性,那么利用下面的创建方式

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"%K == %@", @"code", @2];
    

    就不会被坑

    注意:%KK必须是大写。

    最后

    NSPredicate几乎可以满足所有形式的查询,配合Core Data的数据库查询当然不在话下。NSPredicate用法不只这些,有兴趣的同学可以看下nshipster网站的这篇博文,里面提到了一些我没有涉及到的用法。

  • 相关阅读:
    【洛谷3778】[APIO2017] 商旅(分数规划+Floyd)
    【AT4114】[ARC095D] Permutation Tree(简单题)
    【AT4352】[ARC101C] Ribbons on Tree(容斥+DP)
    【AT4169】[ARC100D] Colorful Sequences(DP)
    【洛谷4581】[BJOI2014] 想法(随机算法)
    【洛谷5659】[CSP-S2019] 树上的数(思维)
    【AT4439】[AGC028E] High Elements(线段树)
    【CF590E】Birthday(AC自动机+二分图匹配)
    【洛谷4298】[CTSC2008] 祭祀(Dilworth定理+二分图匹配)
    【洛谷3774】[CTSC2017] 最长上升子序列(杨表)
  • 原文地址:https://www.cnblogs.com/liumu/p/5380476.html
Copyright © 2020-2023  润新知