object and NSPredicate - iphone

In my application i have a large table of around 12000 entries. I am displaying it on tableview. But the search bar is too slow while doing dynamic search. I have read that NSPredicate method is more permorfant then NSRange.
This is my old the code:
[self.filteredListContent removeAllObjects];
listContent = [[NSArray alloc] initWithArray:[dbAccess getAllBooks]];
for (Book *book in listContent)
{
NSRange range = [book.textBook rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
{
[self.filteredListContent addObject:book];
}
}
My new code:
[self.filteredListContent removeAllObjects];
listContent = [[NSArray alloc] initWithArray:[dbAccess getAllBooks]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF like[c] %#",searchText];
[self.filteredListContent addObject:[listContent filteredArrayUsingPredicate:predicate]];
When i try to execute this code i received this error: "Can't do regex matching on object .'"

I would do something more like...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%k like[c] %#",propertyIAmLookingFor,searchText];

Is your book class a string? If not then you cant use SELF like. You need to substitute the name of the property you are comparing.

Related

NSPredicate against NSArray in iphone

How can I use NSPredicate to filter the values from array ivar against search query ? I manage to use NSPredicate against string ivar. I have one user defined class named as "User" which is used to create User from AddressBook. Here is my code
// User Defined Class named as "User"
// In User.h
#interface User : NSObject {
NSString *firstName;
NSString *lastName;
NSString *company;
UIImage *userImage;
NSArray *phoneNumberArray;
NSArray *emailArray;
NSArray *urlArray;
NSArray *addressArray;
NSString *notes;
NSString *dateOfBirth;
}
// In SearchViewController.m
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
// contactsArray is NSMutableArray
contactsArray = [Search searchContactsWithQuery:searchText];
NSLog(#"contactsArray count => %d",[contactsArray count]);
[contactsTableView reloadData];
}
// In Search.m
+(NSMutableArray*)searchContactsWithQuery:(NSString*)query {
NSLog(#"Query => %#",query);
NSMutableArray* predicates=[[NSMutableArray alloc] init];
// Create all predicates
NSPredicate * firstNamePredicate = [NSPredicate predicateWithFormat:#"firstName contains %#",query];
[predicates addObject:firstNamePredicate];
NSPredicate * lastNamePredicate = [NSPredicate predicateWithFormat:#"lastName contains %#",query];
[predicates addObject:lastNamePredicate];
NSPredicate * companyPredicate = [NSPredicate predicateWithFormat:#"company contains %#",query];
[predicates addObject:companyPredicate];
// Don't know how to use on array
// === START of ARRAY Predicate ====
NSPredicate *phoneNoPredicate = [NSPredicate predicateWithFormat:#"phoneNumberArray IN %#",query];
[predicates addObject:phoneNoPredicate];
NSPredicate *emailPredicate = [NSPredicate predicateWithFormat:#"emailArray contains %#",query];
[predicates addObject:emailPredicate];
NSPredicate *urlPredicate = [NSPredicate predicateWithFormat:#"urlArray contains %#",query];
[predicates addObject:urlPredicate];
NSPredicate *addressPredicate = [NSPredicate predicateWithFormat:#"addressArray contains %#",query];
// === END of ARRAY Predicate ====
NSPredicate *notesPredicate = [NSPredicate predicateWithFormat:#"notes contains %#",query];
[predicates addObject:notesPredicate];
NSPredicate *dobPredicate = [NSPredicate predicateWithFormat:#"dateOfBirth contains %#",query];
[predicates addObject:dobPredicate];
// Add predicates to array
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
NSArray * filteredArray = [APP_DELEGATE.allUsersArray filteredArrayUsingPredicate:compoundPredicate];
return [NSMutableArray arrayWithArray:filteredArray];
}
phoneNumberArray,emailArray,urlArray & addressArray are used because user may have multiple entries for phone no , email, address , url like HOME , WORK, iPhone, Other etc.
How can I use predicates on array? Any kind of help is appreciated. Thanks
You are doing the correct way, but you need to create your Predicates with String values instead of passing the Objects directly.
You need to use %k instead of %# like
[NSPredicate predicateWithFormat:#"firstName contains %k",query];
Please let me know if this helps.

How do I create the right NSPredicate for this kind of request?

Hi the problem goes like this:
I have in CoreData entities that have a title and a relationship to keywords entities.
I need a predicate that helps me to fetch all those entities whose title contains the keywords I type. I have the code below that should do this but it doesn't:
NSArray *keywords = [searchString componentsSeparatedByString:#" "];
NSString *predicateString = #"";
for(NSInteger i = 0; i < [keywords count]; i++) {
if(((NSString*)[keywords objectAtIndex:i]).length != 0) {
if(i==0) {
predicateString = [keywords objectAtIndex:i];
}
else {
predicateString = [predicateString stringByAppendingFormat:#" and keywords.normalizedKeyword contains[cd] %#", [keywords objectAtIndex:i]];
}
}
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"keywords.normalizedKeyword contains[cd] %#",predicateString];
For example I have entities with titles like this:
1) "Great Coloring theme"
2) "Theme for kids"
3) "Cars for kids"
my keywords db will contain:
great
coloring
theme
for
kids
cars
How can I create a predicate so when I type for example:
Theme for
the result will be 2) and 3)
or if I type:
great theme
the result will be 1) and 2)
Any help in getting the right predicate to achieve this is very much appreciated. What I tried to do there it doesn't work and I am out of ideas.
Thanks!
I have found the answer myself. To solve such a problem you have to use a NSCompoundPredicate.
The solution to my problem was this:
NSArray *keywords = [lowerBound componentsSeparatedByString:#" "];
NSMutableArray *predicates = nil;
for(NSInteger i = 0; i < [keywords count]; i++) {
if(((NSString*)[keywords objectAtIndex:i]).length != 0) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"keywords.normalizedKeyword contains[cd] %#", [keywords objectAtIndex:i]];
if(predicates == nil) {
predicates = [[NSMutableArray alloc] initWithCapacity:0];
}
[predicates addObject:predicate];
}
}
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];

NSCompoundPredicate fails to match

I'm building a NSPredicate using the code below for an iPhone app. The logging shows the prediate to be: location CONTAINS "head" AND shape CONTAINS "oval" AND texture CONTAINS "bumpy" AND colour CONTAINS "red"
I get no results. If I limit the predicate to a single item it will work, more than 1 fails.
Can anyone tell me why?
Many thanks
NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
for (Ditem in self.tableDataSource) {
NSString *Title = [Ditem valueForKey:#"Title"];
NSString *Value = [Ditem valueForKey:#"Value"];
if([[Value lowercaseString] isEqualToString: #"all"]){
Value = #"";
}
else{
NSPredicate *p = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:[Title lowercaseString]] rightExpression:[NSExpression expressionForConstantValue:[Value lowercaseString]] modifier:NSDirectPredicateModifier type:NSContainsPredicateOperatorType options:0];
[subPredicates addObject:p];
}
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
NSLog(#"predicate: %#", predicate);[self.fetchedResultsController.fetchRequest setPredicate:predicate];
Your predicate is requiring that all of the values in your filterable objects be strings. Is that correct?
Also, I would simplify your subpredicate creation to:
NSPredicate * p = [NSPredicate predicateWithFormat:#"%K CONTAINS %#", [Title lowercaseString], [Value lowercaseString]];

Help creating a predicate for use with filteredArrayUsingPredicate

I am trying to learn how to use predicates and so am trying to replace the following working code with filteredArrayUsingPredicate...
[filteredLocations removeAllObjects];
for (NSString *location in locations) {
NSRange range = [location rangeOfString:query options:NSCaseInsensitiveSearch];
if (range.length > 0) {
[filteredLocations addObject:location];
}
}
Instead I am trying....
[filteredLocations removeAllObjects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains %#", searchText];
[filteredLocations addObjectsFromArray: [locations filteredArrayUsingPredicate:predicate]];
I am not getting the same results with the predicate as I am with for loop rangeOfString. With the range of string for example searchText returns an 8 item array while with the same value returns only 2 with the predicate. Another example, hono will find honolulu in the locations array while it will not find anything using the predicate.
As I understand it SELF represents the object object being evaluated ie. the locations array, so I think that is the correct syntax.
Any help would be appreciated
Thanks, John
I thought that in your for loop, you specify an option NSCaseInsensitiveSearch but in the predicate you didn't.
You can try with this one
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(SELF contains[cd] %#)", searchText];
More details can be looked at here
NSPredicate Tutorial

NSPredicates, scopes and SearchDisplayController

Building a search with some custom objects and three scopes: All, Active, and Former. Got it working with the below code:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString *)scope {
[[self filteredArtists] removeAllObjects];
for (HPArtist *artist in [self artistList]) {
if ([scope isEqualToString:#"All"] || [[artist status] isEqualToString:scope]) {
NSComparisonResult result = [[artist displayName] compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame) {
[[self filteredArtists] addObject:artist];
}
}
}
}
This works fine and takes scope into account. Since I wanted to search four fields at at time, this question helped me come up with the below code:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString *)scope {
[[self filteredArtists] removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"familyName CONTAINS[cd] %# OR familyKanji CONTAINS[cd] %# OR givenName CONTAINS[cd] %# OR givenKanji CONTAINS[cd] %#", searchText, searchText, searchText, searchText];
[[self filteredArtists] addObjectsFromArray:[[self artistList] filteredArrayUsingPredicate:resultPredicate]];
}
However it no longer takes scope into account.
I have been playing around with if statements, adding AND scope == 'Active', etc. to the end of the statement and using NSCompoundPredicates to no avail. Whenever I activate a scope, I'm not getting any matches for it.
Just a note that I've seen approaches like this one that take scope into account, however they only search inside one property.
Here's about how I would do it:
NSPredicate * template = [NSPredicate predicateWithFormat:#"familyName CONTAINS[cd] $SEARCH "
#"OR familyKanji CONTAINS[cd] $SEARCH "
#"OR givenName CONTAINS[cd] $SEARCH "
#"OR givenKanji CONTAINS[cd] $SEARCH"];
- (void)filterContentForSearchText:(NSString *)search scope:(NSString *)scope {
NSPredicate * actual = [template predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:search forKey:#"SEARCH"]];
if ([scope isEqual:#"All"] == NO) {
NSPredicate * scopePredicate = [NSPredicate predicateWithFormat:#"scope == %#", scope];
actual = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:actual, scopePredicate, nil]];
}
[[self filteredArtists] setArray:[[self artistList] filteredArrayUsingPredicate:actual]];
}