NSPredicate过滤数组数据
NSPredicate
编写软件时,经常需要获取一个对象集合,然后删除不满足条件的对象,保留符合条件的对象,从而提供一些有意义的对象。
Cocoa提供了一个名为NSPredicate的类,他用于指定过滤器的条件。可以创建NSPredicate对象,通过该对象准确地描述所需的条件,对每个对象通过谓词进行筛选,判断他们是否与条件相匹配。
Cocoa用NSPredicate描述查询的方式,原理类似于在数据库中进行查询。可以在数据库风格的API中使用NSPredicate类,例如Core Data和Spotlight。可以将NSPredicate看成另一种间接操作方式。例如,入股哦需要查询满足条件的机器人,可以使用谓词对象进行检查,而不必使用代码进行显式查询。通过交换谓词对象,可以使用通用代码对数据进行过滤,而不必对相关条件进行硬编码。
这也是开放/关闭原则的另一个应用。
17.1 创建谓词
可以通过两种基本方式来实现。第一种是创建许多对象,并将他们结合起来。还需要使用大量代码,如果正在构建通用用户接口来指定查询,采用这种方式比较简单。
另一种方式是查询代码中的字符串。对初学者来说,这种方式比较简单。因此,本书中我们终点介绍查询字符串。常见的面向字符串的API警告信息适用于查询字符串,特别适用于缺少编译器错误检查及有时出现奇怪的运行时错误等情况。
我们仍然使用CarParts示例,本章的示例基于上一章创建的汽车车库示例。
首先看一下一辆汽车的情况:
Car *car;
car=makeCar(@"Herbie",@"Honda",@"CRX",1984,2,110000,58);
[garage addCar:car];
现在创建谓词:
NSPredicate *predicate;
predicate=[NSPredicate predicateWithFormat:@"name== 'Herbie'"];
+predicateWithFormat:来实际创建谓词。可以使用单引号,双引号需要进行转义
计算谓词
BOOL match=[predicate evaluateWithObject:car];
NSLog(@"%s",(match)?"YES":"NO");
-evaluateWithObject:通知接收对象根据指定的对象计算自身的值。在本例中,接收对象为car,使用name做为键路径,应用valueForKeyPath:方法获取名称。然后,他将自身的值与"Herbie"相比较。如果名称和"Herbie"相同,则-evaluateWithObject:返回YES,否则返回NO。
以下是另一个谓词:
predicate=[NSPredicate predicateWithFormat:@"engine.horsepower>150"];
match=[predicate evaluateWithObject:car];
如果检查对象集合,情况会变得更加有趣:
NSArray *cars=[garage cars];
for (Car *car in cars) {
if ([predicate evaluateWithObject:car]){
NSLog(@"%@",car.name);
}
}
合理吗?不!在继续介绍以下内容之前,我们先要确保理解了这里涉及的所有语法。仔细查看NSLog中的汽车名称调用。他使用了Obj-C 2.0的点语法,这与调用[car name]是等效的。
17.2 燃料过滤器
编程人员最显著的优点/缺点都是懒惰。如果我们不必编写for循环和if语句,这有什么不好吗?幸运的是,某些类别将谓词过滤方法添加到了Cocoa集合类中。
-filteredArrayUsingPredicate:是NSArray数组中的一个类别方法,他将循环过滤数组内容,根据谓词计算每个对象的值,并将值为YES的对象累积到将被返回的新数组中:
NSArray *results;
results=[cars filteredArrayUsingPredicate:predicate];
NSLog(@"%@",results);
以上这些结果同前面的结果不一样,这里是一组汽车,前面是一组名称,记住valueForKey:发送给数组时,键将作用于数组中的所有元素:
NSArray *names;
names=[results valueForKey:@"name"];
NSLog(@"%@",names);
假设有一个可变数组,你需要剔除不属于该数组的所有项目。NSMutableArray具有-filterUsingPredicate方法,他能轻松实现你的目标:
NSMutableArray *carsCopy=[cars mutableCopy];
[carsCopy filterUsingPredicate:predicate];
如果输出carsCopy,结果将是前面我们看到的3辆汽车的集合。
也可以使用-filteredArrayUsingPredicate:方法和NSMutableArray数组来构建新数组,因为NSMutableArray是NSArray的超类。
NSSets中也有类似的调用方法。
正如我们在讨论KVC时提到的,使用谓词确实很便捷,但是性能上会差一些,尤其是开发iPhone程序,应该随时密切关注程序的性能
17.3 格式说明符
如果需要知道哪些汽车的马力高于200,稍后又需要知道哪些汽车的马力高于50,我们该怎么办?硬编码不是一个好办法。我们可以使用谓词字符串。例如“engine.horsepower>200”和“engine.horsepower>50”,但我们必须重新编译程序,并再次遇到第三章中的麻烦问题。
可以通过两种方式将不同的内容放入谓词格式字符串中:格式说明符和变量名。首先,我们将介绍格式说明符。
predicate=[NSPredicate predicateWithFormat:@"engine.horsepower>%d",50];
当然,我们一般不直接在代码中使用常量值50.
除了使用printf说明符,也可以使用%@插入字符串值,将%@看成是一个引用字符串:
predicate=[NSPredicate predicateWithFormat:@"name==%@",@"Herbie"];
注意,这里的格式字符串中没有引用%@。如果你需要引用%@,例如"name='%@'",应该将%@放在谓词字符串中。
通过NSPredicate字符串,也可以使用%K指定键路径。该谓词和其他谓词相同,使用name=='Herbie'做为条件:
predicate=[NSPredicate predicateWithFormat:@"%K==%@",@"name",@"Herbie"];
为了构造灵活的谓词,一种方式是使用格式说明符,另一种方式是将变量名放入字符串中,
NSPredicate *predicateTemplate=[NSPredicate predicateWithFormat:@"name==$NAME"];
现在,我们有一个含有变量的谓词。接下来,可以使用predicateWithSubstitutionVariables调用来构建新的专用谓词,创建一个键/值对字典。其中,键是变量名(不包含美元符号$),值是插入谓词的内容,代码如下搜索:
NSDictionary *varDict;
varDict=[NSDictionary dictionaryWithObjectsAndKeys:@"Herbie",@"NAME",nil];
predicate=[predicateTemplate predicateWithSubstitutionVariables:varDict];
可以使用不同的对象做为变量名称,例如NSNumber。以下谓词用于过滤引擎的功率:
predicateTemplate=[NSPredicate predicateWithFormat:@"engine.horsepower>$POWER"];
varDict=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:150],@"POWER",nil];
predicate=[predicateTemplate predicateWithSubstitutionVariables:varDict];
除了使用NSNumber和NSString之外,也可以使用[NSNull null]设置nil值,甚至可以使用数组。
注意,不能用$VARIABLE作为键路径,因为他只表示值。使用谓词格式字符串时,如果想通过程序改变键路径,需要使用$K格式说明符。
谓词机制不执行类型检查。你也许会在输入数字的地方不小心插入字符串,这样就会出现运行时错误信息,或者出现其他不可预知的错误。
17.4 运算符
NSPredicate的格式字符串包含大量不同的运算符,这里将介绍大多数。其余的运算符可以通过苹果公司的在线文档进行查询。
17.4.1 比较和逻辑运算符
==和=、>、>=和=>、<、<=和=<、!=和<>
此外,谓词字符串语法还支持括号表达式和AND、OR、NOT逻辑运算符或者C样式的等效表达式&&、||和!
示例:
predicate=[NSPredicate predicateWithFormat:@"(engine.horsepower>50) AND (engine.horsepower<200)"];
谓词字符串中的运算符不区分大小写。你可以随意使用AnD、And或or。
不等号既适用于数字值又适用于字符串值。如果需要按字母顺序从开始查看所有汽车,可以使用以下谓词:
predicate=[NSPredicate predicateWithFormat:@"name<'Newton'"];
results=[cars filteredArrayUsingPredicate:predicate];
NSLog(@"%@",[results valueForKey:@"name"]);
17.4.2 数组运算符
predicate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN {50,200}"];
花括号表示数组,BETWEEN将数组中的第一个元素看成是数组的下界,第二个元素看成是数组的上界。
可以使用%@格式说明符插入你自己的NSArray对象:
NSArray *betweens=[NSArray arrayWithObjects:
[NSNumber numberWithInt:50],
[NSNumber numberWithInt:200],nil];
predicate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN %@",betweens];
也可以使用变量:
predicateTemplate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN $POWERS"];
varDict=[NSDictionary dictionaryWithObjectsAndKeys:betweens,@"POWERS",nil];
predicate=[predicateTemplate predicateWithSubstitutionVariables:varDict];
数组并不仅仅用来指定某个区间的端点值,你可以使用IN运算符查找数组中是否含有某个特定值,具有SQL编程经验的编程人员应该对以下代码非常熟悉:
predicate=[NSPredicate predicateWithFormat:@"name IN {'Herbie','Snugs','Badger','Flap'}"];
17.5 SELF 足够了
某些时候,可能需要将谓词应用于简单的值(例如那些纯文本老式字符串),而并非那些可以通过键路径进行操作的对象。假设我们有一个汽车名称数组,并且需要应用前面相同的过滤器,从NSString对象中查询name时,将不能起到预期效果。那么,我们用什么来代替name呢?
用SELF来解决!SELF可以引用用于谓词计算的对象。事实上,我们可以将谓词中所有的键路径表示成对应的SELF。此谓词和前面的谓词完全相同,代码如下所示:
predicate=[NSPredicate predicateWithFormat:@"SELF.name IN {'Herbie','Snugs','Badger','Flap'}"];
现在,再回到那个字符串数组。如果某个字符串也在名称数组中,该怎么办呢?我们来分析一下。
首先,需要从某处获取仅含有名称的数组,因为已经熟悉了CarParts中的各种对象,因此,我们将借助于数组,使用KVC技术获取valueForKey:方法,以便处理这些对象:
NSArray *names=[cars valueForKey:@"name"];
predicate=[NSPredicate predicateWithFormat:@"SELF IN {'Herbie','Snugs','Badger','Flap'}"];
results=[names filteredArrayUsingPredicate:predicate];
这里有一个问题,以下代码将输出什么结果呢?
NSArray *names1=[NSArray arrayWithObjects:@"Herbie",@"Badger",@"Judge",@"Elvis",nil];
NSArray *names2=[NSArray arrayWithObjects:@"Judge",@"Paper Car",@"Badger",@"Finto",nil];
predicate=[NSPredicate predicateWithFormat:@"SELF IN %@",names1];
results=[names2 filteredArrayUsingPredicate:predicate];
NSLog(@"%@",results);
输出:
{
Judge,
Badger
}
对于取两个数组的交集的运算而言,这是一种很巧妙的方式。
17.6 字符串运算符
前面介绍字符串时,我们介绍过关系运算符。此外,还有一些针对字符串的关系运算符:
BEGINSWITH
ENDSWITH
CONTAINS
例如:
"name BEGINSWITH 'Bad'" 匹配 "Badger" ,使用"name ENDSWITH 'vis'" 匹配 "Elvis",以及 "name CONTAINS 'udg'"
这些匹配是区分大小写的,也区分重音符。为了减少名称匹配规则,可以为这些运算符添加[c]、[d]或[cd]修饰符。其中,c表示“不区分大小写”,d表示“不区分发音符号”,[cd]表示都不区分。例如 "name BEGINSWITH[cd] 'HERB'"
17.7 LIKE运算符
通配符: ? 匹配单个字符 * 匹配任意个字符
"name LIKE '*er'" 匹配任何包含er的名称,等效于CONTAINS
"name LIKE '???er*'"
另外,LIKE也接受[cd]修饰符
如果你热衷于正则表达式,可以使用MATCHES运算符。赋给该运算符一个正则表达式,谓词将会计算出他的值。
from:http://supershll.blog.163.com/blog/static/370704362012116946365/
NSPredicate过滤数组数据的更多相关文章
- pandas数组和numpy数组在使用索引数组过滤数组时的区别
numpy array 过滤后的数组,索引值从 0 开始. pandas Series 过滤后的 Series ,保持原来的索引,原来索引是几,就是几. 什么意思呢,来看个栗子: import num ...
- Elasticsearch去重查询/过滤重复数据(聚合)
带家好,我是马儿,这次来讲一下最近遇到的一个问题 我司某个环境的es中被导入了重复数据,导致查询的时候会出现一些重复数据,所以要我们几个开发想一些解决方案,我们聊了聊,相出了下面一些方案: 1.从源头 ...
- PHP批量过滤MYSQL数据库内站外链接和图片
因发现站内很多引用站外文章的链接失效,产生大量的死链接,对于搜索引擎来说是极不友好的,很不利于网站优化,所以站内添加了站外链接过滤功能,对于新加的文章,在添加入库时就自动增加rel="nof ...
- wireshark如何过滤 http数据包
http.host==magentonotes.com http.host contains magentonotes.com //过滤经过指定域名的http数据包,这里的host值不一定是请求中的域 ...
- ember.js:使用笔记4 数组数据的分组显示
除了之前介绍的将数组数据在一个页面中输出的方法,还可以将数组数据分组,按照点击,在不同页面中分别显示,方法为: Model: 例如:Table Router: 设置一个父对象和子对象设置: this. ...
- 使用sharepreferce记录数组数据
使用sharepreferce记录数组数据 /** * * sharepreference纪录news数据 * * */ private static final String name=" ...
- php中利用array_filter过滤数组为空值
[导读] 在我们开发过程中,判断数组为空时你会想到什么方法呢?首先想到的应该是empty函数,不过直接用empty函数判断为空是不对的,因为当这个值是多维数的时候,empty结果是有值的.其实我们可以 ...
- 【原创】Easyui tree filter 过滤本地数据无效的原因
Easyui tree filter 过滤本地数据无效的解决方式 正确使用方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
- PHP过滤数组中的空值
php对数组的操作已经很完善了,提供给我们很多内置函数用以操作数组,其实可以用array_filter函数对PHP数组中的控制进行过滤 array_filter() 函数用回调函数过滤数组中的值.该函 ...
随机推荐
- 【UVA】536 Tree Recovery(树型结构基础)
题目 题目 分析 莫名A了 代码 #include <bits/stdc++.h> using namespace std; string s1,s2; void buil ...
- enq:TM-contention
enq:TM-contention 2011-08-04 15:55:17 分类: Linux 7.1 enq:TM-contention 执行dml期间,为防止对与dml相关的对象进 ...
- yum ftp本地源
一. 准备工作1. 安装系统centos7.32. 环境 10.10.10.14 controller-1 10.10.10.15 computer-1 3. 在14主机上安装FTP服务yum ins ...
- ceph---luminous版的安装
前言 ceph luminous版本新增加了很多有意思的功能,这个也是一个长期支持版本,所以这些新功能的特性还是很值得期待的,从底层的存储改造,消息方式的改变,以及一些之前未实现的功能的完成,都让ce ...
- gif屏幕录像软件
ScreenToGif:http://screentogif.codeplex.com 备份下载地址:http://flask.pub/screentogif2.zip 本站连接:http ...
- Nginx配置ProxyCache缓存
利用nginx cache缓存网站数据 nginx本身就有缓存功能,能够缓存静态对象,比如图片.CSS.JS等内容直接缓存到本地,下次访问相同对象时,直接从缓存即可,无需访问后端静态服务器以及存储存储 ...
- PHP,JAVA,NET 开发比较
装载出处:http://www.cnblogs.com/sinlang5778/archive/2011/08/10/2133190.html 一.语言: PHP:PHP产生与1994年,其语法混合了 ...
- Image与Bitmap的区别及相互转换
1. Image.FromFile()返回的是某个继承自Image的具体类的对象,在这里,就是Bitmap或者Metafile其中之一.这应该算是factory pattern的一种形式.所以,Ima ...
- Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA
找到原先备份的项目,把 .idea 文件夹重新覆盖,解决问题.
- proxmox 安装ROS 备忘
虚拟机设置:使用qemu64 CPU和vrtio网卡在家里测试性能最好.