Searching arrays of objects with NSPredicate - iphone

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[displayItems removeAllObjects]; //clear array to ensure no repeat info
if ([searchText length] == 0) {
displayItems = (NSMutableArray *)allItems;
}
else {
//search by item category
NSPredicate *catPredicate = [NSPredicate predicateWithFormat:#"category
CONTAINS[cd] %#",searchText];
[searchable filterUsingPredicate:catPredicate];
//further search by item name
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd]
%#",searchText];
[searchable filterUsingPredicate:namePredicate];
displayItems = searchable;
searchable = (NSMutableArray *)allItems;
}
[self.searchResults reloadData];
}
This method is part of a simple searchable table view I am trying to create for a larger project. My code compiles and runs, and when i type something into the search bar the search function appears to work, but then the program crashes as soon as a second letter is typed. If I type two letters in a row, it throws 'NSInvalidArgumentException', reason: '-[_NSArrayI filterUsingPredicate:]: unrecognized selector sent to instance 0x6d6c040', but if I type one letter and then hit enter or backspace, it throws this guy 'NSInvalidArgumentException', reason: '-[_NSArrayI removeAllObjects]: unrecognized selector sent to instance 0x6a7f300' when I type a second letter.
I am pretty new to objective-c, and this has me perplexed. Any help I could get would be greatly appreciated.... :-/ Still having issues since update.

"One does not simply cast NSArray into NSMutableArray and then call NSMutableArray methods on it" - Boromir
Create a mutable copy instead, like this:
searchable = [allItems mutableCopy];
NOTE: Make sure to release searchable when you are finished with it.

You have to use NSMutableArray to call the methods.
NSArray has a method "filteredArrayusingPredicate".
The simple solution is use NSMutableArray.

Related

fetchObjectsForEntityName: not recognised as valid method

This code is really confusing me. When I type in fetchObjectsForEntityName, it doesn't offer suggestions, and then afterwards gives me a warning of No visible #interface for 'NSManagedObjectContext' declares the selector 'fetchObjectsForEntityName:withPredicate:'.
Here's the code:
-(SyncObject *)objectWithSyncID:(NSString *)syncID inContext:(NSManagedObjectContext *)context {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"syncID == %#", syncID];
NSSet *set = [context fetchObjectsForEntityName:#"SyncObject" withPredicate:predicate];
Because the method doesn't exist. I guess you copied some code from a tutorial but you didn't copy all of it. There should be a category on NSManagedObjectContext which defines and implements the method.

How to perform searching based on characters starting in ios

Hi in my ipad application am performing searching operation using searchbar.For performing thise searching operation i am using following logic
If any element includes the character what i am entering in search bar, those elements have to pickup from so many elements and display in table.
code:
NSRange rTextRange = [finalSearchingString rangeOfString:searchStr options:(NSCaseInsensitiveSearch)];`
if (range.location != NSNotFound){
[self.filterdList addObject:product];
}
here final results avaialble in filterdList array and that array data i will display in tableview.
Now my issue in this operation is actually now the requirement is changed. New requirement is The product which are starting with searching characters those only have to display not included products. Please help me how to implement this functionality .Thanks in advance.
try like this i Hope this one helps you,
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", txtSearch.text];
NSArray *ResultArray = [yourArray filteredArrayUsingPredicate:predicate];
[tableview reloadData];
kept above code in searchBar delegate method and reload the tableview with result array.

NSOperationQueue operations filteredArrayUsingPredicate error

I have class MyOperation : NSOperation with #property (nonatomic, retain) NSString *oID;
And sometimes I need to cancel operation with specific oID. I'm trying to do this:
NSArray *operations = operationQueue.operations;
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat: #"oID == %#", _specificID]];
NSArray *arrayOperations = [operations filteredArrayUsingPredicate: predicate];
and get error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "oID == 0f5db97b-f127-4425-ad79-451d1f204016"'
Is it possible to filter operation from NSOperationQueue?
Your problem is that you are formatting a string, then sending that to the Predicate constructor method. It's already a formatter, and knows how to format the data you give it.
Basically, you need the format to look like:
oID == "0f5db97b-f127-4425-ad79-451d1f204016"
but you are getting
oID == 0f5db97b-f127-4425-ad79-451d1f204016
If you use the formatter by itself, you should get past this issue...
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"oID == %#", _specificID];
NOTE: The predicate formatter knows it should handle strings specially, and automatically adds the extra quotation characters when you pass a string to be formatted by %#'
I'm going to guess that the operations.queue is a mutable array (logging its class returns this '__NSArrayM'). What you should try is:
NSArray *operations = [NSArray arrayWithArray:operationQueue.operations];
That said, some operation may have completed (and been removed from the mutable queue) by the time the predicate is applied to it.

[_UITableViewSeparatorView rangeOfString:]: unrecognized selector sent to instance

I am having a problem in executing the following code while searching in the table. This code works fine elsewhere. But currently it is giving an error as
[_UITableViewSeparatorView rangeOfString:]: unrecognized selector sent to instance 0x6041790
Following is the code that is troubling me. Please let me know the bug gidden in there.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if([searchText isEqualToString:#""] || searchText==nil)
{
[displayTable reloadData];
return;
}
NSInteger counter = 0;
for(NSString *name in dataSource)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
if(r.location== 0)//that is we are checking only the start of the names.
{
[tableData addObject:name];
}
}
counter++;
[pool release];
}
[displayTable reloadData]; }
Thanks in advance!!
Looking forward to your responses.
thanks
It looks like you're over-releasing the strings that you have stored in dataSource. I would check any place that you use/create those strings to make sure that you aren't releasing them more times than you should.
It means that the memory where the string should reside in memory was freed and there is another object on that place (_UITableViewSeparatorView in your case). Make sure that you are not over-releasing the string in array
You can try to search with NSZombiesEnabled in instruments: link
What are you putting in dataSource? Evidently, it contains an object that is not an NSString.

UISearchDisplayController and search performance with lots of data

I'm trying to figure out the best way to perform a fast search using a UISearchDisplayController.
I have a plist file with more than 36000 entries. I load this file in a dictionary and I perform the search in this dictionary. It works but it's kinda slow and there is lag between each touch event. I want an autocompletion effect so I need the search to be triggered for each touch event.
I tried using thread to perform the search in background with the following code :
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[NSThread detachNewThreadSelector:#selector(filter:) toTarget:self withObject:searchString];
return NO;
}
// Filter function looks like this
-(void) filter:(NSString *)search {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self.filteredList removeAllObjects]; // empty array of results
for (NSString *s in self.keys ) {
NSComparisonResult result = [s compare:search options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [search length])];
if (result == NSOrderedSame) {
[self. filteredList addObject:s ];
}
}
[ self.searchDisplayController.searchResultsTableView reloadData];
[pool release];
}
But my application crashes randomly with the following message:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (0).
I'm sure it's because I dont use threading properly.
I also tried using [self performSelector:#selector(filter:) withObject:searchString afterDelay:0.5]; but I'm also facing application crashes.
What is the best way to handle this ? I'm not really good with threads but I think it's the best way to go, isn't it ? I also tried a solution with SQLite but the application is still not so responsive.
My data are actually zip codes and cities (36000 unique different cities but 6500 unique zip codes since multiple cities can have the same zip code). I want my search item to be either a zip code or a city name. I know that one big dictionary is definitely not the best structure. How could I organize my data for more efficiency?
Thank you for helping me with this.
The problem is that your search string is longer than one of your original strings in the array. When comparing from 0 to [search length] you are falling outside of s. You should first make sure that s is longer than search:
for (NSString *s in self.keys ) {
if ([s length]>=[search length]) {
NSComparisonResult result = [s compare:search options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [search length])];
if (result == NSOrderedSame) {
[self. filteredList addObject:s ];
}
}
}