I have an entity with three attributes: studyID:string, studyName:string and studyDate:date.
I need to create a predicate for searches but the user has the option to enter any possible combination of all three attributes e.g. one search is on studyID and studyDate but not studyName while the next search is just on studyID.
How to I create a predicate dynamically depending on what combination of attributes the user decides to search on?
You need to create a compound predicate whose final form depends on which attributes the user searches on.
The cleanest solution is to build the predicate in code using NSExpression, NSComparisonPredicate and NSCompoundPredicate. That will let you customize the predicate for each search.
Something like this:
-(NSPredicate *) predicateWithStudyID:(NSString *) aStudyID studyName:(NSString *) aStudyName date:(NSDate *) aDate{
NSPredicate *p;
NSMutableArray *preds=[[NSMutableArray alloc] initWithCapacity:1];
if (aDate != nil) {
NSExpression *dateKeyEx=[NSExpression expressionForKeyPath:@"studyDate"];
NSExpression *aDateEx=[NSExpression expressionForConstantValue:aDate];
NSPredicate *datePred=[NSComparisonPredicate predicateWithLeftExpression:dateKeyEx
rightExpression:aDateEx
modifier:NSDirectPredicateModifier
type:NSEqualToPredicateOperatorType
options:0];
[preds addObject:datePred];
}
if (![aStudyID isEqualToString:@""]) {
NSExpression *studyIDKeyEx=[NSExpression expressionForKeyPath:@"studyID"];
NSExpression *aStudyIDEx=[NSExpression expressionForConstantValue:aStudyID];
NSPredicate *studyIDPred=[NSComparisonPredicate predicateWithLeftExpression:studyIDKeyEx
rightExpression:aStudyIDEx
modifier:NSDirectPredicateModifier
type:NSLikePredicateOperatorType
options:NSCaseInsensitivePredicateOption];
[preds addObject:studyIDPred];
}
if (![aStudyName isEqualToString:@""]) {
NSExpression *studyNameKeyEx=[NSExpression expressionForKeyPath:@"studyName"];
NSExpression *aStudyNameEx=[NSExpression expressionForConstantValue:aStudyName];
NSPredicate *studyNamePred=[NSComparisonPredicate predicateWithLeftExpression:studyNameKeyEx
rightExpression:aStudyNameEx
modifier:NSDirectPredicateModifier
type:NSLikePredicateOperatorType
options:NSCaseInsensitivePredicateOption];
[preds addObject:studyNamePred];
}
p=[NSCompoundPredicate andPredicateWithSubpredicates:preds];
[preds release];
return p;
}
Then you would use it like so:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDate *firstDate=[NSDate date];
NSDate *secondDate=[firstDate dateByAddingTimeInterval:60*60*24];
NSDate *thirdDate=[secondDate dateByAddingTimeInterval:60*60*24];
// I use an array of dictionaries for convenience but it works
// for managed objects just as well.
NSDictionary *d1=[NSDictionary dictionaryWithObjectsAndKeys:
@"abc123", @"studyID",
@"firstStudy", @"studyName",
firstDate, @"studyDate",
nil];
NSDictionary *d2=[NSDictionary dictionaryWithObjectsAndKeys:
@"abc456", @"studyID",
@"secondStudy", @"studyName",
secondDate, @"studyDate",
nil];
NSDictionary *d3=[NSDictionary dictionaryWithObjectsAndKeys:
@"abc789", @"studyID",
@"thirdStudy", @"studyName",
thirdDate, @"studyDate",
nil];
NSArray *a=[NSArray arrayWithObjects:d1,d2,d3, nil];
NSPredicate *p=[self predicateWithStudyID:@"" studyName:@"thirdStudy" date:thirdDate];
NSLog(@"p = %@",p);
NSArray *filtered=[a filteredArrayUsingPredicate:p];
NSLog(@"%@",filtered);
return YES;
}
See the Predicate Programming Guide: Creating Predicate Directly in Code
@DaveDelong below points out that in the first block of code above, my creation of the subpredicates is overly complex. There is no need in this case (other than execution speed) to generate the subpredicates from expressions. Instead just build the subpredicates as normal and then compound them like so:
-(NSPredicate *) predicateWithStudyID:(NSString *) aStudyID studyName:(NSString *) aStudyName date:(NSDate *) aDate{
NSPredicate *p;
NSMutableArray *preds=[[NSMutableArray alloc] initWithCapacity:1];
if (aDate != nil) {
NSPredicate *datePred=[NSPredicate predicateWithFormat:@"studyDate==%@",aDate];
[preds addObject:datePred];
}
if (![aStudyID isEqualToString:@""]) {
NSPredicate *studyIDPred=[NSPredicate predicateWithFormat:@"studyID Like %@",aStudyID];
[preds addObject:studyIDPred];
}
if (![aStudyName isEqualToString:@""]) {
NSPredicate *studyNamePred=[NSPredicate predicateWithFormat:@"studyName Like %@",aStudyName];
[preds addObject:studyNamePred];
}
p=[NSCompoundPredicate andPredicateWithSubpredicates:preds];
[preds release];
return p;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With