I am trying to make a compound predicate for my core data search. So when the user enters text in the search bar, it will display results for anything that has that text in either the name, optionOne or optionTwo attributes.
I tried this:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
if (self.sBar.text !=nil) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(name contains[cd] %#) || (optionOne contains[cd] %#) || (optionTwo contains[cd] %#)", self.sBar.text];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
[self.myTable reloadData];
[sBar resignFirstResponder];
}
But it just crashes without a descriptive reason. So I think I need to take these three predicates and somehow combine them:
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:#"name contains[cd] %#", self.sBar.text];
NSPredicate *optionOnePredicate = [NSPredicate predicateWithFormat:#"optionOne contains[cd] %#", self.sBar.text];
NSPredicate *optionTwoPredicate = [NSPredicate predicateWithFormat:#"optionTwo contains[cd] %#", self.sBar.text];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(name contains[cd] %#) || (optionOne contains[cd] %#) || (optionTwo contains[cd] %#)", self.sBar.text];
Since you have 3 %# tokens in your string, you need to have 3 self.sBar.text expressions at the end.
Alternatively, you could do something like this:
NSPredicate *template = [NSPredicate predicateWithFormat:#"name contains[cd] $SEARCH OR optionOne contains[cd] $SEARCH OR optionTwo contains[cd] $SEARCH"];
NSDictionary *replace = [NSDictionary dictionaryWithObject:self.sBar.text forKey:#"SEARCH"];
NSPredicate *predicate = [template predicateWithSubstitutionVariables:replace];
This is handier if you're building this predicate a lot, because you can store the "template" predicate in an ivar. Parsing a predicate is not the snappiest of things, and using the template version means you'll only have to parse it once (instead of every time the search bar's text changes).
Related
I'm trying to write an NSPredicate that expresses: "If uid contains myID OR targetUser"
However no matter what I try, it doesn't seem to want to work. How can I edit my line below to execute the above?
My current code:
NSPredicate *p3 = [NSPredicate predicateWithFormat:#"uid contains[cd] %#", myID];
I tried the following, but no dice:
NSPredicate *p3 = [NSPredicate predicateWithFormat:#"uid contains[cd] %# OR contains[cd] %#", myID, targetUser];
NSPredicate *p3 = [NSPredicate predicateWithFormat:#"(uid contains[cd] %#) OR (uid contains[cd] %#)", myID, targetUser];
Try this one :
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"uid contains[cd] %# OR uid contains[cd] %#", myID, targetUser];
I have built an NSPredicate similar to that below before and it worked perfectly. However, this time, it is generating the error:
'NSRangeException', reason: '-[NSTaggedPointerString characterAtIndex:]: Index 1 out of bounds; string length 1'
Here is how I build the predicate:
NSString *shortTitleClause = nil;
NSString *longTitleClause = nil;
Edit:
Adding clauses:
shortTitleClause =[NSString stringWithFormat:#"(shorttitle contains[c] %#)", searchText];
longTitleClause =[NSString stringWithFormat:#"(longtitle contains[c] %#)", searchText];
//My actual predicate has many more clauses but am getting same error no matter how many.
NSMutableArray *predArr = [#[] mutableCopy];
if (shortTitleClause.length > 0){
[predArr addObject:shortTitleClause];
}
if (longTitleClause.length > 0){
[predArr addObject:longTitleClause];
}
NSString *predStr = [predArr componentsJoinedByString:#"||"];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:predStr];
searchResults = [theBooks filteredArrayUsingPredicate:resultPredicate];
When searching for the letter 'S' the predicate logs as: `(shorttitle contains[c] S)||(longtitle contains[c] S)`
It is not clear to me what the taggedPointerString refers to me. Can anyone see why I am getting this error?
You cannot use stringWithFormat to replace %# values when preparing predicates. Use predicateWithFormat instead. And don't join them using componentsJoinedByString - use NSCompoundPredicate to combine multiple predicates:
shortTitlePredicate = [NSPredicate predicateWithFormat:#"(shorttitle contains[c] %#)", searchText];
longTitlePredicate = [NSPredicate predicateWithFormat:#"(longtitle contains[c] %#)", searchText];
NSPredicate *resultPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[shortTitlePredicate,longTitlePredicate]];
I have an NSPredicate looks like that:
NSPredicate *predicateSearch;
if ([category_array count]>0 && [location_array count]>0) {
predicateSearch =[NSPredicate predicateWithFormat:#"category_id IN %# AND location_id IN %# AND company_title CONTAINS %#",category_array,location_array,searchStr];
}
else if ([category_array count]>0){
predicateSearch =[NSPredicate predicateWithFormat:#"category_id IN %# AND company_title CONTAINS %#",category_array,searchStr];
}
else if([location_array count]>0){
predicateSearch = [NSPredicate predicateWithFormat:#"location_id IN %# AND company_title CONTAINS %#",location_array,searchStr];
}
[fetchRequest setPredicate:predicateSearch];
Since NSPredicate is not able to handle Nil values I am using this If-Statement to format the Predicate and handle the Nilvalue as 'ANY'.
The issue here is that I want also the searchStr to be handled as 'ANY' if it's Nil.
I tried to set it as empty string in case of Nil but it won't work.
Is there any way to accomplish that without using any If-statements for each case(9 if-statements it's too much)?
Use compound predicates. Example, based on what you posted:
NSPredicate *categoryPredicate = (category_array.count > 0)
? [NSPredicate predicateWithFormat:#"category_id IN %#", category_array]
: [NSPredicate predicateWithValue:YES];
NSPredicate *locationPredicate = (category_array.count > 0)
? [NSPredicate predicateWithFormat:#"location_id IN %#", location_array]
: [NSPredicate predicateWithValue:YES];
NSPredicate *searchPredicate = (searchStr.length > 0)
? [NSPredicate predicateWithFormat:#"company_title CONTAINS %#", searchStr]
: [NSPredicate predicateWithValue:YES];
NSCompoundPredicate *predicate =
[NSCompoundPredicate
andPredicateWithSubpredicates:#[
categoryPredicate,
locationPredicate,
searchPredicate,
]
];
I have an array of objects with name and address.
Both properties are of type NSString.
But when i'm using the compound predicate with both results i don't get any result. If i'm using just one predicate it works very well.
Has anyone an idea whats going wrong here in my code? thnx
-(void)filterContentForSearchString:(NSString*) searchText
{
NSPredicate *resultsPredicate = [NSPredicate predicateWithFormat:#"SELF.name like[cd] %#", searchText];
NSPredicate *resultsPredicate2 = [NSPredicate predicateWithFormat:#"SELF.place like[cd] %#", searchText];
NSPredicate *compoundpred = [NSCompoundPredicate andPredicateWithSubpredicates:#[resultsPredicate, resultsPredicate2]];
self.searchResults = [self.companies filteredArrayUsingPredicate:compoundpred];
}
Can you try using a single predicate declaration
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"SELF.name like[cd] %# OR SELF.place like[cd] %#", searchText, searchText];
and filter the array with it directly
self.searchResults = [self.companies filteredArrayUsingPredicate:predicate];
i'm using a NSPredicate to filter an array in my searchbar. This is my code which works perfectly.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[cd] %#",searchText];
filteredArray = [NSMutableArray arrayWithArray:[storesArray filteredArrayUsingPredicate:predicate]];
The problem is i want to add a second condition as the name which is the address. How can i do this? I've tried this, but then none of the predicates are working.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[cd] %#",searchText];
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"SELF.address contains[cd] %#",searchText];
NSPredicate *fullPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[predicate, predicate1]];
filteredArray = [NSMutableArray arrayWithArray:[storesArray filteredArrayUsingPredicate:fullPredicate]];
You can use the OR comparison operator:
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"SELF.name contains[cd] %# OR SELF.address contains[cd] %#", searchText, searchText];