「OC」NSPredicate —— 使用谓词过滤元素

「OC」NSPredicate —— 使用谓词过滤元素

前言

在暑假的3Gshared和学生管理系统之中,无论是登陆注册界面还是学生信息的输入,都存在需要我们对输入内容进行限制,于是我们就需要找到一个方法来解决对输入内容的判断限制,就是NSPredicate——谓词来过滤输入的元素。由于在暑假的几个任务之中只是知道如何使用,并没有仔细了解,乘着暑假剩下的空闲时间进行学习总结。

介绍

NSPredicate类是用来定义逻辑条件约束的获取或内存中的过滤搜索,该对象可以准确的描述所需条件,对每个对象通过谓词进行筛选,判断是否与条件相匹配。使用NSPredicate可以有效地过滤和查询数据,提高数据处理的效率和灵活性。在Core Data中,NSPredicate常用于构建Fetch请求,从数据库中检索符合特定条件的数据。同时,在使用集合类(如NSArray、NSDictionary)时,NSPredicate也可以用来对数据进行过滤和排序。

首先我们需要了解构建谓词的常用运算符

请添加图片描述

此处为运算符的总结,看起来有点无从下手是吧,别急,后文在用到的时候还会再将功能贴出

了解完运算符,我们就可以了解如何使用NSPredicate,谓词表达式由表达式、运算符和值构成。

  1. predicateWithFormat:创建一个谓词。
+ (id)predicateWithFormat:(NSString *)format...;
  1. evaluateWithObject:接受对象,根据指定的对象计算自身的值。
-(BOOL)evaluateWithObject:(id)value;

谓词的一个常用功能就是对集合进行过滤,当程序使用谓词对集合进行过滤时,程序将会自动遍历集合元素,并根据集合元素来计算谓词的值,只有当根据某个集合元素计算谓词并返回YES时,该集合元素才会被保留下来。

//使用指定的谓词过滤NSArray集合,返回符合条件的元素组成的新集合
- (NSArray*)filteredArrayUsingPredicate:(NSPredicate *)predicate;

//使用指定的谓词过滤NSMutableArray,剔除集合中不符合条件的元素
- (void)filterUsingPredicate:(NSPredicate *)predicate;


常见用法

比较运算符

=、==:判断两个表达式是否相等,在谓词中===相同的意思都是判断,而没有赋值这一说

NSNumber *num = @123;
NSPredicate *pre = [NSPredicate predicateWithFormat:@"SELF == 123"];//此处 = 与 == 的功能是相同的
if ([pre evaluateWithObject:num]) {
    NSLog(@"1");
} else {
    NSLog(@"2");
}

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

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

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

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

!=、<>:判断两个表达式是否不相等

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

BETWEEN的用法:

NSNumber *num = @123;
NSPredicate *pre = [NSPredicate predicateWithFormat:@"SELF BETWEEN {40,123}"];
if ([pre evaluateWithObject:num]) {
    NSLog(@"1");
} else {
    NSLog(@"2");
}

逻辑运算符

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

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

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

AND、&&

NSArray *testArray = @[@1, @2, @3, @4, @5, @6];
 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF > 2 && SELF < 5"];//此处AND 和 &&效果相同
 NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];
 NSLog(@"filterArray:%@", filterArray);

NOT、 !:此处用了IN,不了解的读者可以先往后看

NSArray *filterArray = @[@"ab", @"abc"];
 NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];
 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];
 NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);//取了array之中与filterArray的非交集

字符串比较运算符

  • BEGINSWITH:检查某个字符串是否以指定的字符串开头
  • ENDSWITH:检查某个字符串是否以指定的字符串结尾
  • CONTAINS:检查某个字符串是否包含指定的字符串
  • LIKE:检查某个字符串是否匹配指定的字符串模板。其之后可以跟?代表一个字符和代表任意多个字符两个通配符。比如"name LIKE ‘ac’",这表示name的值中包含ac则返回YES;"name LIKE '?ac’",表示name的第2、3个字符为ac时返回YES。?代表的是一个未知符号,*可以代表模糊搜索不指定个数,可以看看下文的例子
  • MATCHES:检查某个字符串是否匹配指定的正则表达式。值得一提的是正则表达式的执行效率是最低的,但其功能是最强大的,也是我们最常用的。

下面我们将每个比较运算符都进行举例(对于比较运算符来说,是不用区分大小写的,即 BEGINSWITH 和 beginwith 相同

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

BEGINSWITH:

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"Nick", @"Ben", @"Adam", @"Melissa",@"alice",@"你好",@"啊",nil];//定义数组
    NSArray *tempArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH 'a'"]];//过滤以a开头的元素, 如果为BEGINSWITH[c]则代表忽略大小写
    NSLog(@"tempArray:%@", tempArray);

ENDSWITH

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"Nick", @"Ben", @"Adam", @"Melissa",@"alice",@"你好",@"啊",nil];//定义数组
NSArray *tempArray1 =  [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF ENDSWITH[c] 'a'"]]; //以a结尾的元素
NSLog(@"tempArray1:%@", tempArray1);

CONTAINS

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"Nick", @"Ben", @"Adam", @"Melissa",@"alice",@"你好",@"啊",nil];//定义数组
 NSArray *tempArray2 = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"]]; //包含有e的元素
NSLog(@"tempArray1:%@", tempArray2);

LIKE

过滤中间有e 的元素

NSArray *tempArray3 =   [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF like[cd] '*e*' "]];

过滤以N开头的元素

NSArray *tempArray5 =   [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF like[cd] 'N*' "]];

MATCHES

检验手机号码的程序

- (BOOL)checkPhoneNumber:(NSString *)phoneNumber
{
    NSString *regex = @"^[1][3-8]\\d{9}$";
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    return [pred evaluateWithObject:phoneNumber];
}

关于正则表达式的使用,有兴趣的读者可以看一下以下文章:

正则表达式在IOS中的应用及IOS中三种正则表达式的使用与比较

聚合运算符

  • ANY、SOME:集合中任意一个元素满足条件,就返回YES。
  • ALL:集合中所有元素都满足条件,才返回YES。
  • NONE:集合中没有任何元素满足条件就返回YES。如:NONE person.age < 18,表示person集合中所有元素的age>=18时,才返回YES。
  • IN:只有当左边表达式或值出现在右边的集合中才会返回YES。

IN:(聚合运算符之中的举例默认都为IN示例之中的array1和array2)

NSArray *array1 = [NSArray arrayWithObjects:@1,@2,@3,@4,@5,@6,@7,@8, nil];
NSArray *array2 = [NSArray arrayWithObjects:@4,@6, nil];
NSArray *temp = [array1 filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF in%@",array2]]; //表示获取 array2 和 array1中的交集

ANYSOME

检查 array1 中是否有任意一个元素出现在 array2 中:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY SELF in %@", array2];
BOOL hasCommonElement = [predicate evaluateWithObject:array1];
if (hasCommonElement) {
    NSLog(@"array1 中至少有一个元素出现在 array2 中");
} else {
    NSLog(@"array1 和 array2 没有交集");
}

在实际使用中,ANYSOME 是等价的,通常只使用 ANY

ALL

检查 array1 中的所有元素是否都出现在 array2 中:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL SELF in %@", array2];
BOOL allInArray2 = [predicate evaluateWithObject:array1];
if (allInArray2) {
    NSLog(@"array1 中的所有元素都出现在 array2 中");
} else {
    NSLog(@"array1 中至少有一个元素不在 array2 中");
}

NONE

检查 array1 中是否没有任何元素出现在 array2 中:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE SELF in %@", array2];
BOOL noneInArray2 = [predicate evaluateWithObject:array1];
if (noneInArray2) {
    NSLog(@"array1 中没有任何元素出现在 array2 中");
} else {
    NSLog(@"array1 和 array2 有交集");
}

用于字典或者类当中

我们还可以使用谓词去筛选字典元素或者是类之中的属性。

NSArray *employees = @[
    @{@"name": @"啊", @"age": @25, @"department": @"HR"},
    @{@"name": @"哦", @"age": @35, @"department": @"IT"},
    @{@"name": @"额", @"age": @40, @"department": @"IT"}
];

// 创建 NSPredicate 对象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 30 AND department == 'IT'"];

// 应用 NSPredicate 过滤数组
NSArray *filteredEmployees = [employees filteredArrayUsingPredicate:predicate];

// 处理过滤后的结果
for (NSDictionary *employee in filteredEmployees) {
    NSLog(@"Name: %@, Age: %@, Department: %@", employee[@"name"], employee[@"age"], employee[@"department"]);
}

在类之中使用,我们先创建一个Person类,里面包含name和age两个属性,我们重写description方法,以及写了一个初始化方法

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property NSString *name;
@property NSInteger age;

+ (id)personWithName:(NSString *)name andAge:(NSInteger)age;

@end

#import "Person.h"

@implementation Person

+ (id)personWithName:(NSString *)name andAge:(NSInteger)age{
    Person *person = [[Person alloc] init];
    person.name = name;
    person.age = age;
    return person;
}

- (NSString *)description{
    NSString *s =[NSString stringWithFormat:@"name = %@,age = %ld",_name,_age];
    return s;
}

@end

接下来创建一个由Person类组成的数组,进行创建谓词进行过滤

#import <Foundation/Foundation.h>
#import "Person.h"

//谓词,指定过滤器的条件,将符合条件的对象保留下来
//一般用谓词过滤数组中指定的元素
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        NSArray *persons = [NSArray arrayWithObjects:
                            [Person personWithName:@"啊" andAge:20],
                            [Person personWithName:@"1" andAge:30],
                            [Person personWithName:@"2" andAge:40],
                            [Person personWithName:@"3" andAge:50],
                            [Person personWithName:@"4" andAge:60],
                            [Person personWithName:@"5" andAge:70],
                            [Person personWithName:@"6" andAge:20],
                            [Person personWithName:@"7" andAge:40],
                            [Person personWithName:@"8" andAge:60],
                            [Person personWithName:@"9" andAge:40],
                            [Person personWithName:@"0" andAge:80],
                            [Person personWithName:@"10" andAge:90],
                            [Person personWithName:@"1" andAge:20]];
        
        //年龄小于30
        //定义谓词对象,谓词对象中包含了过滤条件
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < %d",30];
        //使用谓词条件过滤数组中的元素,过滤之后返回查询的结果
        NSArray *array = [persons filteredArrayUsingPredicate:predicate];
        NSLog(@"filterArray = %@",array);
      
      
}

成功运行我们打印出一下结果

请添加图片描述

格式说明符(占位符)

谓词的表达式中,如果要动态修改条件,可以使用占位符来实现对谓词的拼接

在使用时,如果需要拼接属性名,其占位符为%K(注意大写)而不是%@,如:

NSString * key = @"age";
int age = 30;
//拼接示例:
[NSPredicate predicateWithFormat:@"%K < %d", key, age];

除此之外,在程序之中我们过滤条件一般来说也是根据需求进行动态变化的,所以

除此之外,还可以在谓词表达式中使用动态改变的属性值,就像环境变量一样

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < $AGE"];

上述表达式中,开头,随着程序改变$AGE这个谓词表达式的比较条件就可以,使用predicateWithSubstitutionVariables:动态改变。

接下来我们就可以学习一下,如何对这个较为重要的运算符进行使用

//用$AGE进行占位,可以动态修改$对应的值,这里的AGE可以是任意字符串
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < $AGE"];

//修改AGE的值(AGE对应上面的$后的字符串),生成新的NSPredicate对象
NSPredicate *newPredicate = [predicate predicateWithSubstitutionVariables:@{@"AGE":@18}];

//使用newPredicate过滤数组
NSArray *array = [persons filteredArrayUsingPredicate: newPredicate];

实际运用

关于谓词的实际运用,我现在只是简单的对谓词进行了初步的使用,即使用正则表达式,即使用关键字MATCHES,进行格式的规范,当出现不符合格式的内容则弹出警告框,具体的限制代码如下

- (BOOL)validateName:(NSString *)name {
    NSString *pattern = @"^[\\p{Han}]{1,5}$";//限制只能为汉字
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
    return [predicate evaluateWithObject:name];
}

- (BOOL)validateClass:(NSString *)className {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"^[\u4e00-\u9fa5]{2,3}[0-9]{4}$"];//限制只能为汉字2-3个加上4位数字
    return [predicate evaluateWithObject:className];
}

- (BOOL)validateGrade:(NSString *)grade {
    // 正则表达式,匹配只包含数字的字符串
    NSString *regex = @"^[0-9]+$";
    NSPredicate *regexPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    
    BOOL isNumber = [regexPredicate evaluateWithObject:grade];
    
    if (isNumber) {
        // 如果是数字,检查其整数值是否小于200
        NSInteger value = [grade integerValue];
        return value < 200;
    }
    
    return NO;
}

总结

本篇文章作为在学生管理系统之中用谓词实现正则表达式限制文本框输入的延伸,其实一开始觉得这个正则表达式限制是一个比较小的点,深入了解才知道谓词这个我感觉比较重要的知识点,也没想到内容会有这么多,也希望以后可以使用谓词在其它方面可以帮助到我。在暑假剩下的时间还是得好好加油了!!!

参考文章

iOS中谓词(NSPredicate)的基本入门使用教程

iOS NDPredicate与数组过滤

【OC梳理】NSPredicate

OC学习篇之—谓词(NSPredicate)

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用NSPredicate的Swift代码示例,用于将大量数据按时间分组: ```swift struct DataItem { let name: String let timeStamp: TimeInterval // 时间戳 } func groupDataByTime(_ data: [DataItem]) -> [[DataItem]] { // 将数据按时间先后排序 let sortedData = data.sorted { $0.timeStamp < $1.timeStamp } // 创建空字典 var groups = [String: [DataItem]]() // 使用NSPredicate过滤并分组数据 let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" for item in sortedData { let date = Date(timeIntervalSince1970: item.timeStamp) let dateString = dateFormatter.string(from: date) let predicate = NSPredicate(format: "SELF.dateString == %@", dateString) let filteredArray = (groups as NSDictionary).filtered(using: predicate) if var items = filteredArray.first?.value as? [DataItem] { items.append(item) groups[dateString] = items } else { groups[dateString] = [item] } } // 将分组后的数据按时间先后顺序排序 let sortedGroups = groups.sorted { $0.key < $1.key } // 将所有分组添加到一个数组中 var result = [[DataItem]]() for (_, items) in sortedGroups { result.append(items) } return result } ``` 在这个示例中,我们同样定义了一个`DataItem`结构体来表示数据的名称和时间戳。`groupDataByTime`函数首先将数据按时间先后排序,然后使用`NSPredicate`过滤并分组数据。我们指定`NSPredicate`的格式为`SELF.dateString == %@`,其中`dateString`是我们根据时间戳计算得到的时间字符串,`%@`是用于替换的占位符。通过对字典进行过滤,我们可以得到一个包含指定时间字符串的数组,然后将新的数据项添加到这个数组中。最后,我们将所有分组添加到一个大数组中,按时间先后顺序排序,并返回结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值