Searching Arrays With NSPredicate and Blocks
来源:互联网 发布:台式电脑品牌推荐知乎 编辑:程序博客网 时间:2024/05/01 10:59
Searching Arrays With NSPredicate and Blocks
In researching a change I wanted to make to the example RSS Reader app that I have working on for this blog I started looking at the best ways to search and filter arrays. I have previously posted on filtering arrays with NSPredicate so I will only briefly revisit that topic. More interesting is using some of the newer block based methods introduced with iOS 4.0 to efficiently search a collection of objects.
Filtering Arrays with NSPredicate.
The NSArray class has the method filteredArrayUsingPredicate: which will return a new NSArray containing the objects for which a predicate returns true. So for example if I have an array of NSStrings I can get a new array containing only the strings starting with the letter ‘a’ (case insensitive) as follows:
NSArray *names = [[NSArray alloc] initWithObjects:@"Alpha", @"Bravo", @"Charlie", nil];NSPredicate *predicate = [NSPredicate predicateWithFormat: @"SELF beginswith[c] 'a'"];NSArray *aNames = [names filteredArrayUsingPredicate:predicate];
In situations where we have a collection of more complex objects, for example a Person class with an ivar named “lastName” we can also create a predicate that queries the ivar. So to create a new array containing all people whose last name begins with ‘a’:
NSArray *people = [[NSArray alloc] initWithObjects:me, you, nil];NSPredicate *preda = [NSPredicate predicateWithFormat: @"lastName beginswith[c] 'a'"];NSArray *aList = [people filteredArrayUsingPredicate:preda];
In fact you can follow any key-path in a predicate. So if my Person class contains a reference to an Employer object a query to find all Person objects working for “Apple” would like this:
NSPredicate *applePred = [NSPredicate predicateWithFormat: @"employer.name == 'Apple'"];NSArray *appleEmployees = [people filteredArrayUsingPredicate:applePred];
The NSMutableArray class has an additional method filterUsingPredicate: that is used the same way but with the important difference that the array is filtered in place. So instead of getting back a new NSArray the NSMutableArray is modified so that it only contains objects that match the predicate. If you do not want to modify the original NSMutableArray you can of course use filteredArrayUsingPredicate: to get back a new NSArray.
Searching an Array Using Blocks
So far this is nothing new, but there are many situations where you just need to know if something already exists in a collection and you do not need to get back the matching objects. In this situation using filteredArrayUsingPredicate returns an array that we do not need.
One option is to go back to iterating over the array applying a test for the condition we are interested in (I had such an example in my last post):
NSString *query = @"Alpha";BOOL found = NO;for (Person *person in people) { if ([person.lastName isEqualToString:query]) { found = YES; }}
This looks ideal for replacing with a predicate – but one where we do not get back an array:
- (BOOL)personExists:(NSString *)key withValue:(NSString *)value { NSPredicate *predExists = [NSPredicate predicateWithFormat: @"%K MATCHES[c] %@", key, value]; NSUInteger index = [self.people indexOfObjectPassingTest: ^(id obj, NSUInteger idx, BOOL *stop) { return [predExists evaluateWithObject:obj]; }]; if (index == NSNotFound) { return NO; } return YES;}
In this case I am using a predicate with a dynamic property name that is substituted for the %K parameter in the format string. This is perfect for situations where you want to change the property name you are searching for at run-time.
The new step is the use of the indexOfObjectPassingTest: method which takes a block as its argument and returns an index to the first object that matches the predicate or NSNotFound if no object matches. The syntax for the block is a little hard to understand. The general structure of the method is as follows:
[myArray indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) { return [obj someTestForValue: value]; }];
The start of the block is identified by the “^” character and what follows are the arguments that will be passed to the block. Each object in the array is then processed in turn by calling the contents of the block with the block arguments. So for example, the obj variable can be used to access the current object from the array, idx gives the index into the array of the array and finally the stop boolean allows you to abort the processing of the array from within the block.
We can put any test we like into the content of the block as long as it returns a BOOL. In our case we can use a predicate and pass it the current object for evaluation using the evauateWithObject method. I leave it for you to decide if the approach using Predicates and Blocks is easy to understand compared to the simpler approach of iterating through the array. I have to say that I still find the syntax for Objective-C blocks to be especially ugly but since more and more of the Cocoa frameworks are adopting blocks it is worth starting to get the hang of them.
- Searching Arrays With NSPredicate and Blocks
- Sorting and Searching
- log and searching
- Searching and Ranking
- Searching and Sorting
- Searching and Sorting
- Searching and Sorting
- Searching and Sorting
- Searching vulnerable sites with Google
- Sorting and Searching (1)----bubbleSort
- searching (informed and non-informed)
- Searching: Linear Probing And Insertion
- Searching: Tree Search And Insertion
- Working with Blocks
- Circular Reference with Blocks
- Working with Blocks
- 数组 Arrays and associative arrays
- delegations, blocks and notifications
- 通过java批量修改文件的修改时间
- 使用IB时设置textView属性崩溃
- 果合发布年度报告《2013中国iOS市场发展趋势及手游开发六大核心策略》
- 用ActiveMQ+MQTT实现Android点对点消息通知
- ios的画面切换的动画效果
- Searching Arrays With NSPredicate and Blocks
- 关于BaseAdapter,SimpleAdapter的数据显示错乱问题,选中状态错乱问题,Checkbox选中状态错乱问题总结
- Andorid中C/C++调用Bionic库问题
- 调试grunt方法
- Android 线程池
- A.jsp跳转到B.jsp传递值方法的扩展(结合jquery mobile)
- MIME协议分析
- ios define NSLog debug 应用发布nslog注释
- 用RVCT编译STM32点亮LED