NSPredicate(谓词),是一个Foundation类,可以使用它指定数据被获取或者过滤的方式。它的查询语言就像SQL的WHERE和正则表达式的交叉类似,来定义一个集合被搜寻的逻辑条件。

在集合中使用NSPredicate

Foundation中提供了使用NSPredicate来过滤NSArray/NSMutableArray&NSSet/NSMutableSet的方法。

不可变的集合,NSArray&NSSet,有可以通过评估接收到的predicate来返回一个不可变集合的方法filteredArrayUsingPredicate:filteredSetUsingPredicate:。 可变集合,NSMutableArray&NSMutableSet,可以使用方法filterUsingPredicate:,它可以通过运行接收到的谓词来移除评估结果为FALSE的对象。

NSArray *array = @[@"jim",@"cook",@"jobs",@"sdevm"];
    NSPredicate *preLength = [NSPredicate predicateWithFormat:@"length > 3"];
    NSLog(@"array = %@",array);
    NSLog(@"array precidate=%@",[array filteredArrayUsingPredicate:preLength]);

    NSMutableArray *muArray = [NSMutableArray arrayWithObjects:@"jim",@"cook",@"jobs",@"sdevm", nil];
    [muArray filterUsingPredicate:preLength];
    NSLog(@"muArray=%@",muArray);  

常用筛选方法:

利用成员实例的方法 ,类似上面的方法。

NSArray *array2 = @[@"2",@"3",@"4",@"5"];
    NSPredicate *preIntergeris3 = [NSPredicate predicateWithFormat:@"integerValue >= 3"];
    NSPredicate *preIntergeris3_2 = [NSPredicate predicateWithFormat:@"integerValue >= %@",@3];
    NSLog(@"array2 with preIntergeris3 = %@",[array2 filteredArrayUsingPredicate:preIntergeris3]);  

扩展到对象类:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;
@end
@implementation Person
- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end


NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];
NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

    Person *person = [[Person alloc]init];
    person.firstName = [firstNames objectAtIndex:idx];
    person.lastName = [lastNames objectAtIndex:idx];
    person.age= [ages objectAtIndex:idx];
    [people addObject:person];
}];

NSPredicate *precidate1 = [NSPredicate predicateWithFormat:@"firstName = 'Bob'"];
NSPredicate *precidate2 = [NSPredicate predicateWithFormat:@"lastName = %@", @"Smith"];
NSPredicate *precidate3 = [NSPredicate predicateWithFormat:@"age >= 30"];

// ["Bob Jones"]
NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:precidate1]);

// ["Alice Smith", "Charlie Smith"]
NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:precidate2]);

// ["Charlie Smith", "Quentin Alberts"]
NSLog(@"30's: %@", [people filteredArrayUsingPredicate:precidate3]);  

三个Predicate的含义是:

对数组中的成员对象Person对象,使用[Person firstName] (firstName的get方法),返回=Bob的对象 。
对数组中的成员对象Person对象,使用[Person lastName] (firstName的get方法),返回=Smith的对象 。
对数组中的成员对象Person对象,使用[Person age] (firstName的get方法),返回>=30的对象 。

这里针对NSString的操作,使用的是=号,这里好像和==效果是一样的。 来判断字符串是否相同。那么如何判断字符串包含另外一个字符串呢?可以在NSPredicate中使用CONTAINS(大小写都可以)来表示包含关系。

当判断的时候需要忽略大小写可以使用[cd] >[c],忽略重音符使用[cd] >[d],即不区分大小写,写不区分发音符号则使用[cd]

NSPredicate *precidateContainsali = [NSPredicate predicateWithFormat:@"firstName contains[cd] %@",@"ali"];
  // ["Alice Smith"]
  NSLog(@"contains ali = %@",[people filteredArrayUsingPredicate:precidateContainsali]);

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

BEGINSWITH(以某个字符串开头,begins with) && ENDSWITH(ends with 以某个字符串结尾)

NSPredicate *precidateBeginsWith = [NSPredicate predicateWithFormat:@"firstName beginswith[cd] %@",@"a"];

   NSLog(@"Beginswith ali = %@",[people filteredArrayUsingPredicate:precidateBeginsWith]);

   NSPredicate *precidateEndWith = [NSPredicate predicateWithFormat:@"firstName endswith[cd] %@",@"e"];

   NSLog(@"EndWith ali = %@",[people filteredArrayUsingPredicate:precidateEndWith]);

通配符LIKE *代表一个或者多个或者是空字符, ?代表一个字符 。

NSPredicate *predicateLike = [NSPredicate predicateWithFormat:@"firstName Like[cd] %@",@"??I*"];
   //["Alice Smith"]
   NSLog(@"Like ??i = %@",[people filteredArrayUsingPredicate:predicateLike]);

关系运算 IN ,BETWEEN ,AND ,OR,NOT IN

  1. IN判断是否在之中**
  2. OR(或,可以用||代替),OR可以判断不同的属性,
  3. BETWEEN(之间),通常用来判断NSNumber对象。
  4. AND(且),可以使用&&代替
  5. NOT(非,可以用!代替)
NSPredicate *inPredicate = [NSPredicate predicateWithFormat:@"firstName in %@",@[@"Alice",@"Sgslg"]];
   //["Alice Smith"]
   NSLog(@"inpredicate = %@",[people filteredArrayUsingPredicate:inPredicate]);

   NSPredicate *orPredicate = [NSPredicate predicateWithFormat:@"firstName == %@ OR firstName == %@",@"Alice",@"Aharlie"];
   //["Alice Smith"],["Aharlie Smith"]
   NSLog(@"inpredicate = %@",[people filteredArrayUsingPredicate:orPredicate]);

   NSPredicate *orPredicate2 = [NSPredicate predicateWithFormat:@"firstName == %@ OR lastName == %@",@"Alice",@"Jones"];
   //["Alice Smith"],["Bob Jones"]
   NSLog(@"inpredicate = %@",[people filteredArrayUsingPredicate:orPredicate2]);

NOT最常用的用法是从一个数组中剔除另外一个数组的数据,这个可以使用一个双层循环,但是非常麻烦。但是用NSPredicate 加NOT运算符就非常简单了。 如代码:

NSArray *arrayFilter = @[@"abc1", @"abc2"];
    NSArray *arrayContent = @[@"a1", @"abc1", @"abc4", @"abc2"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@" self in %@",arrayFilter];
    NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"NOT(self in %@)",arrayFilter];
    //[" abc1"],["abc2"]
    NSLog(@"predicate1= %@",[arrayContent filteredArrayUsingPredicate:predicate]);
    //[" a1"],["abc4"]
    NSLog(@"predicate2= %@",[arrayContent filteredArrayUsingPredicate:predicate2]);

##(NSPredicate)predicateWithBlock:(BOOL (^)(id evaluatedObject, NSDictionary bindings))block

事实上,因为block可以封装任意的计算,所以有一个查询类是无法以NSPredicate格式字符串形式来表达的(比如对运行时被动态计算的值的评估)。而且当同一件事情可以用NSExpression结合自定义选择器来完成时,block为完成工作提供了一个方便的接口。

NSPredicate *blockPredicate = [NSPredicate predicateWithBlock:^BOOL(id  _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
       return [[evaluatedObject lastName] length] <= 5;

   }];
   NSLog(@"Short Names = %@",[people filteredArrayUsingPredicate:blockPredicate]);

重要提示:由predicateWithBlock:生成的NSPredicate不能用于由SQLite存储库支持的Core Data数据的提取要求。

NSCompoundPredicate

我们见过与&或被用在谓词格式字符串中以创建复合谓词。然而,我们也可以用NSCompoundPredicate来完成同样的工作。

下面的谓词是相等的:

NSPredicate *orPredicate = [NSPredicate predicateWithFormat:@"firstName == %@ OR firstName == %@",@"Alice",@"Aharlie"];
   //["Alice Smith"],["Aharlie Smith"]
   NSLog(@"inpredicate = %@",[people filteredArrayUsingPredicate:orPredicate]);
   NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"firstName == %@",@"Alice"],[NSPredicate predicateWithFormat:@"firstName == %@",@"Aharlie"]]];
   NSLog(@"compoundPredicate = %@",[people filteredArrayUsingPredicate:compoundPredicate]);

匹配用法

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];  

比较常用的还是配合正则表达式,列举几个常用的正则表达式:

1.是否以a开头以e结尾。

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

2.是否是邮箱

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

3、是否是手机号:(包含了181,1700,1709,1705字段)

+ (BOOL)isMobileNumber:(NSString *)mobileNum
{
    /**
     *  中国移动:China Mobile
     *
     *  134[0-8],135,136,137,138,139,147,150,151,157,158,159,182,183,187,188,1705
     *
     */
    NSString * CM = @"^1((34[0-8]|(3[5-9]|5[017-9]|8[2378])|47\\d)|705)\\d{7}$";
    /**
     *  中国联通:China Unicom
     *
     *  130,131,132,152,155,156,185,186,1709
     *
     */
    NSString * CU = @"^1((3[0-2]|5[256]|8[56])[0-9]|709)\\d{7}$";
    /**
     *  中国电信:China Telecom
     *
     *  133,1349,153,180,189,181,177,1700
     *
     */
    NSString * CT = @"^1((33|53|77|8[019])[0-9]|349|700)\\d{7}$";
    NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
    NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
    NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
    if (
        ([regextestcm evaluateWithObject:mobileNum] == YES)
        || ([regextestcu evaluateWithObject:mobileNum] == YES)
        || ([regextestct evaluateWithObject:mobileNum] == YES)
        ){
        return YES;
    }
    else{
        return NO;
    }
}

###谓词语法:

替换: %@是对值为字符串,数字或者日期的对象的替换值,
%K是key path的替换值。

NSPredicate *Kpredicate = [NSPredicate predicateWithFormat:@"%@= %@",@"age",@33];
//output  Kpredicate : "age" == 33
   NSLog(@"Kpredicate : %@",Kpredicate);
   NSPredicate *Kpredicate2 = [NSPredicate predicateWithFormat:@"%K= %@",@"age",@33];
   //output: Kpredicate : age == 33
   NSLog(@"Kpredicate : %@",Kpredicate2);

   // ["Aharlie Smith"]
   NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:Kpredicate2]);