How to with create a compoundPredicate with both "OR" and "AND" - iphone

I have the following 3 categories fetched, but want to add a "AND" predicate to narrow the results, where the "mark" is on.
NSMutableArray *questionNumberArray=[[NSMutableArray alloc] init];
// Fetch questions
NSManagedObjectContext *context = self.document.managedObjectContext;
NSFetchRequest *fetchRequest =
[[NSFetchRequest alloc] initWithEntityName:#"Questions"];
//building the predicate with selected category
NSMutableArray *parr = [NSMutableArray array];
if ([cat0str length]){
[parr addObject:[NSPredicate predicateWithFormat:#"question.category CONTAINS[c] %#",cat0str]];
}
if ([cat1str length]){
[parr addObject:[NSPredicate predicateWithFormat:#"question.category CONTAINS[c] %#",cat1str]];
}
if ([cat2str length]){
[parr addObject:[NSPredicate predicateWithFormat:#"question.category CONTAINS[c] %#",cat2str]];
}
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:parr];
[fetchRequest setPredicate:compoundPredicate];
NSPredicate *markPredicate =[NSPredicate predicateWithFormat:#"question.mark == 1"];
}
//I'd like to do something like that:
NSPredicate *finalPredicate = [compoundPredicate && markPredicate];
[fetchRequest setPredicate:finalPredicate];

Is this what you are looking for?
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects:compoundPredicate, markPredicate, nil]];
or, using the "modern Objective-C" syntax for container literals:
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:
#[compoundPredicate, markPredicate]];

Related

object and NSPredicate

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.

Comparing an Array with another using NSPredicate

I've the following structure:
TxnSummary * t1 = [[TxnSummary alloc] init];
t1.txnId = #"1";
t1.shortDesc = #"First one";
t1.filters = [[NSArray alloc] initWithObjects:#"F1", #"F2", nil];
TxnSummary * t2 = [[TxnSummary alloc] init];
t2.txnId = #"2";
t2.shortDesc = #"Second one";
t2.filters = [[NSArray alloc] initWithObjects:#"F1",#"F2", #"F3", nil];
TxnSummary * t3 = [[TxnSummary alloc] init];
t3.txnId = #"3";
t3.shortDesc = #"Third one";
t3.filters = [[NSArray alloc] initWithObjects:#"F1", #"F3", nil];
TxnSummary * t4 = [[TxnSummary alloc] init];
t4.txnId = #"4";
t4.shortDesc = #"Fourth one";
t4.filters = [[NSArray alloc] initWithObjects:#"F4", nil];
NSArray * xnArray = [[NSArray alloc] initWithObjects:t1,t2,t3,t4, nil];
Now if I want to find out which of the txn summaries have filters F1, then I could do this:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"filters CONTAINS[cd] %#", #"F1"];
NSArray * filteredArray = [xnArray filteredArrayUsingPredicate:predicate];
This works well if I'm comparing for only one string, but if want to find out which all txn summaries have filters "F1", or "F2", then if I have to follow the above mechanism I'll have to create two predicates - each for F1 and F2 and then run it against the xnArray (which seems to be inefficient). I want to be able to create a list of filters strings and use that to fetch the matching txs from the xn array.
NSArray * filterStrings = [[NSArray alloc] initWithObjects:#"F1",#"F2", nil];
Does NSPredicate have functionality to achieve this or should I resort to some other method of filtering?
Appreciate your help.
Thanks, Kumar
If I understand you correctly, you can achieve this by creating a compound predicate from an array of predicates, for example:
NSPredicate *newFilterPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:selectedItemIDs];
EDIT: added more detailed explanation:
Compound predicates combine predicates into one predicate. For example, if you want to filter for items that contain "F1" or "F2" you do this:
// Normally build this in some kind of loop
NSPredicate *firstPredicate = [NSPredicate predicateWithFormat:#"filter =%#", #"F1"];
NSPredicate *secondPredicate = [NSPredicate predicateWithFormat:#"filter =%#", #"F1"];
// Create the array of predicates
NSArray *arrayOfPredicates = [NSArray arrayWithObjects:firstPredicate, secondPredicate, nil];
// Create the compound predicate
NSPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:arrayOfPredicates];
There are also methods for "and" instead of "or" as well as other boolean conditions. Full reference can be found here: NSCompoundPredicate Class Reference
Hope this helps,
Dave
You can do something like:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"filters CONTAINS[cd] %# || filters CONTAINS[cd] %#", #"F1", #"F4"];
If you want to add all the keys that are in a array you can do something like that:
NSArray * filterStrings = [[NSArray alloc] initWithObjects:#"F1",#"F4", nil];
NSString* predicateString = [filterStrings componentsJoinedByString:#"'|| filters CONTAINS[cd] '"];
predicateString = [NSString stringWithFormat:#"filters CONTAINS[cd] '%#'",predicateString];
NSPredicate * predicate = [NSPredicate predicateWithFormat:predicateString];
NSArray * filteredArray = [xnArray filteredArrayUsingPredicate:predicate];
I wouldn't use NSArray to store the filters. This is a perfect book-like example for using NSSet/NSMutableSet instead. You can initialize similarly to the array:
t1.filters = [[NSSet alloc] initWithObjects:#"F1", #"F2", nil];
Then you check if that particular string exists simply by calling:
BOOL contains = [t1.filter containsObject:#"F1"];
You can now also filter the set with methods like filteredSetUsingPredicate, objectsPassingTest (to use with blocks) or even create intersections or unions with other sets (isSubsetOfSet, intersectsSet, etc). So for example you could create a new set with the searched elements and check if the set contains them:
NSSet* toFind = [[NSSet alloc] initWithObjects:#"F1", #"F3", nil];
[toFind isSubsetOfSet:t1.filters];
Searching a set is much quicker than an array because set is backed up by a Hash table, whereas an array has to be searched linearly.
If exact matching is OK, you could use the IN predicate like so:
NSArray *filterStrings = [NSArray arrayWithObjects:#"F1", #"F2", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"filters IN %#", filterStrings];
NSArray *filteredArray = [xnArray filteredArrayUsingPredicate:predicate];

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.

Having trouble copying data to a mutable array

I keep getting the error "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[MainViewController minimalFormInContext:]: unrecognized selector sent to class"
from this line of code:
NSLog(#"Accessing specific mine entities");
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Mine" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSError *error = nil;
[request setEntity:entity];
NSPredicate *predicate;
NSPredicate *metalFilter;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *region = [defaults stringForKey:#"mineArray"];
if([region isEqualToString:#"Butte & Plumas"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#)",#"Butte",#"Plumas"];
}
else if([region isEqualToString:#"Sutter, Yuba, & Sierra"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#) OR (county Contains %#)",#"Sutter",#"Yuba",#"Sierra"];
}
else if([region isEqualToString:#"Nevada & Placer"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#)",#"Nevada",#"Placer"];
}
else if([region isEqualToString:#"Sacramento & El Dorado"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#)",#"Sacramento",#"El Dorado"];
}
else if([region isEqualToString:#"San Joaquin, Amador, & Calaveras"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#) OR (county Contains%#)",#"San Joaquin",#"Amador", #"Calaveras"];
}
else if([region isEqualToString:#"Tuolumne & Stanislaus"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#)",#"Tuolumne",#"Stanislaus"];
}
else if([region isEqualToString:#"Merced, Mariposa, & Madera"])
{
predicate = [NSPredicate predicateWithFormat:#"(county Contains %#) OR (county Contains %#) OR (county Contains %#)",#"Merced",#"Mariposa",#"Madera"];
}
[request setPredicate:predicate];
mArray = [[NSMutableArray alloc] init];
mArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
using debugger, I have narrowed down the error as occurring in:
mArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
How do I fix this?
It's likely that it's a retain/release bug. Do "Build and Analyze" in XCode, and improve your code to remove all of the warnings.
Here are things I noticed:
mArray = [[NSMutableArray alloc] init];
mArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
These two lines are very bad. What's your mArray? Does m stands for member, or mutable? If it's a member variable, you shouldn't just assign a new array to that as in
// mArray points to an array at this time, say X
mArray = [[NSMutableArray alloc] init];
// at this point, mArray points to an array Y created by alloc init. X is lost!
Moreover, if you further assign a mutableCopy as you did,
mArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
// at this point, mArray points to an array Z created by mutableCopy. Y is lost, too!
Note that in Objective-C, the variables you see on the source code is just a pointer, not the object itself. If you assign something to a variable, it doesn't make the object perform the assign operation, but it just changes the pointer to point to something different.
The fact that you have these lines suggests you have similar things in various other places; any of it can eventually lead to the bug you're encountering. So you need to deal with them one by one. Good luck!
Another point: when you prepare the variable predicate, the chain of if clauses leaves predicate undefined if region matches none of the choices you listed. This is very dangerous, because in Objective-C, the line
NSPredicate* predicate;
does not initialize predicate to be nil. So it's possible that
[request setPredicate:predicate];
will set a garbage to the predicate of requrest. You should change it to
NSPredicate* predicate=nil;

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]];