How to filter nsdictionary? - nspredicate

I want to search data from nsdictionary based on different criteria.Say I have 4 keys and user can search based on any number of keys ex:4,3 etc..Ex:I have dictionary containing keys(First Name,Last Name,Address,Phone Number).Now user can search based on any key say First Name and Last Name, or only First Name.I am using NSPredicate to search.My problem is how to create dynamic nspredicate?If I provide empty string in
[NSPredicate predicateWithFormat:#"FirstName CONTAINS[cd] %# AND LastNAme CONTAINS[cd] %# AND Address CONTAINS[cd] %# AND PhoneNumber CONTAINS[cd] %#",firstname,lastname,addr,phone]
It does not give any result.How can I achieve this?or I have to create multiple nspredicate based on fields user has provide?
Thanks in advance!

You can build the predicate programmatically:
NSArray *keysToSearch = [NSArray arrayWithObjects:#"FirstName", #"LastName", #"Address", nil];
NSString *searchString = #"Bob";
NSMutableArray *subPredicates = [NSMutableArray array];
for (NSString *key in keysToSearch) {
NSPredicate *p = [NSPredicate predicateWithFormat:#"%K contains[cd] %#", key, searchString];
[subPredicates addObject:];
}
NSPredicate *filter = [NSCompoundPredicate andPredicateWithSubPredicates:subPredicates];
Then use filter to filter your dictionaries.

Related

Core data predicates with key-paths

So i'm trying to work out how to use these predicates, i've read the Apple doc and am trying to use it (https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html) and i have the predicate set up, but it keep getting Thread 1: EXC_BAD_ACCESS (code =) etc.etc.
NSError *error;
NSLog(#"1");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Fruit" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSLog(#"2");
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"Source.sourceName contains[cd] %#", "Apple Tree"];
[fetchRequest setPredicate:predicate];
NSLog(#"3");
NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"4");
testLbl.text = [fetchResult objectAtIndex:0];
Thats the code i'm using, as for the Core Data we have...
Entities Fruit & Source
Attributes fruitName & sourceName
Relationship (one to one) fruitSource<--------->sourceFruit
What i want to do is pull out any fruit that comes from an Apple Tree... >.<
There are two different problems:
To get from Fruit to the related Source you have to use the relationship: #"fruitSource.sourceName contains ..." instead of #"Source.sourceName contains ...".
(This is probably causing the exception.) The %# format requires an Objective-C object as argument, not a C string: #"Apple Tree" instead of "Apple Tree".
So the predicate should look like this:
[NSPredicate predicateWithFormat:#"fruitSource.sourceName CONTAINS[cd] %#", #"Apple Tree"]

avoid duplicate results on Core Data fetch

I have a subclass of the CoreDataTableViewController (subclass of UITAbleViewController dome by the people on Stanford done to link CoreData and TableViews). On this Class, I want to perform a fecth, sorting by an attribute called "definition" and the code which executes it is the following:
- (void)setupFetchedResultsController{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:self.entity];
request.propertiesToFetch=[NSArray arrayWithObject:#"definition"];
request.returnsDistinctResults=YES;
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"%K != nil", #"definition"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"%K != ''", #"definition"];
NSPredicate *predicate3= [NSPredicate predicateWithFormat:#"%K contains[cd] %#", #"definition", self.seachBar.text];
NSArray *prepredicateArray;
if ([self.seachBar.text length]) {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2, predicate3,nil];
}else {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2,nil];
}
request.predicate=[NSCompoundPredicate andPredicateWithSubpredicates:prepredicateArray];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"definition" ascending:YES ]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
If I understood it correctly, setting request.returnsDistinctResults=YES; should avoid fetching duplicates. However it doesn't work and I'm seeing duplicates of this attribute's value.
Is there something I'm missing there? I'd appreciate some pointings there. Thank you in advance.
EDIT: If anyone is having the same issue here, after applying David's answer the resulting fetchedResultsController is just a NSDIctionary with object with only the requested value, which for displaying only purposes is quite fine. One thing I've done in cellForRowAtIndexPath in order to display the results on the cell label is:
Before:
HNMR *hnmr = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=hnmr.definition;
After:
cell.textLabel.text=[[self.fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"definition"];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
This all tells me that the propertiesToFetch is ignored because you haven't set the resultType yourself and the default it to return managed objects instead of dictionaries. Since the propertiesToFetch is ignored the returnsDistinctResults is ignored as well and thus you are still getting duplicates.
Try setting the result type to return dictionaries instead of managed objects.
request.resultType = NSDictionaryResultType;
In addition to David Rönnqvist answer I suggest a useful link (with a sample) on selecting distinct values with Core Data:
core-data-how-to-do-a-select-distinct
Hope that helps.

+[NSPredicate predicateWithFormat] substitute reserved words

Is it possible to substitute reserved words, specifically OR and AND, into a predicateFormat?
I tried:
NSString *andOr = (isAnd ? #"AND" : #"OR"); // isAnd is a BOOL variable
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"firstName == %# %K lastName == %#",
user.firstName, andOr, user.lastName];
But I got:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'Unable to parse the format string "firstName == %# %K lastName == %#"
I tried %s (and also %S) instead of %K but got the same exception.
My working solution for now is to create the predicateFormat separately.
NSString *predicateFormat = [NSString stringWithFormat:
#"firstName == %%# %# lastName == %%#", andOr];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat,
user.firstName, user.lastName];
But, I was wondering if this could be done without creating predicateFormat separately.
Yep, it can:
NSPredicate *firstName = [NSPredicate predicateWithFormat:#"firstName == %#", [user firstName]];
NSPredicate *lastName = [NSPredicate predicateWithFormat:#"lastName == %#", [user lastName]];
NSArray *subpredicates = [NSArray arrayWithObjects:firstName, lastName, nil];
NSPredicate *predicate = nil;
if (isAnd) {
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
} else {
predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
}
Using search bar text i am filtering the data.
Below code is simple and no need of any explanation
NSString *predFormat=[NSString stringWithFormat:#"FirstName CONTAINS[c] '%#' OR LastName CONTAINS[c] '%#' OR Phone CONTAINS[c] '%#'",searchValue,searchValue,searchValue]; //LIKE
NSPredicate *predicate = [NSPredicate predicateWithFormat:predFormat];
NSArray *tempData = [array filteredArrayUsingPredicate:predicate];
Hope this will work for you..
Happy coding....

Sort a NSMutableArray in objective-c?

I am working on NSMutable Array containing list of "First Name" in it.I want to sort it alphabatically.
I tried this code
NSSortDescriptor * sortFriend = [[[NSSortDescriptor alloc] initWithKey:kfirstName ascending:YES] autorelease];
NSArray * descriptors = [NSArray arrayWithObject:sortFriend];
NSArray * sorted = [Friendsarr sortedArrayUsingDescriptors:descriptors];
correct me if i am wrong.I do have an array of names also if that could be used.
Thanks
I tried the code
NSSortDescriptor * sortFriend = [[[NSSortDescriptor alloc] initWithKey:kfirstName ascending:YES selector: #selector(caseInsensitiveCompare:)] autorelease];
NSArray * descriptors = [NSArray arrayWithObject:sortFriend];
NSArray * arrmp = [temparray sortedArrayUsingDescriptors:descriptors];
But still my result contain 2 'k' chars. one 'k' and 'K'.I want to have case insensitive result
If your NSMutableArray only contains objects of type NSString, simply do:
[myMutableArray sortUsingSelector:#selector(compare:)];
You can sort NSMutable array case insensitively by this code
NSSortDescriptor *sorter=[[[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:#selector(caseInsensitiveCompare:)]autorelease];
NSArray *sortdescriptor=[NSArray arrayWithObject:sorter];
[cpy_arr_Service_Name sortUsingDescriptors:sortdescriptor];
I found the solution of sorting NSMutableArrays with sample code which can be found on the site below.
http://www.icodeblog.com/2010/12/10/implementing-uitableview-sections-from-an-nsarray-of-nsdictionary-objects/
I hope it help others to as it provides indexed sorting of a grouped table view using an array as the datasource

Creating predicate with pre-built string

Is there a way to create an nspredicate directly from a pre-formatted string without calling predicateWithFormat? The final string would look something like:
(inpatient=1) AND (dischargedate!=<null>) AND ((attending=SMITH) OR (admitting=SMITH) OR (consulting contains[cd] SMITH) OR (attending=JONES) OR (admitting=JONES) OR (consulting contains[cd] JONES))
NSMutableString *preds = [[NSMutableString alloc] initWithString:#""];
NSArray *provs = [self.providerCode componentsSeparatedByString:#"|"];
for (NSString *prov in provs) {
[preds appendFormat:#" (attending=%#) OR (admitting=%#) OR (consulting contains[cd] %#) ", prov, prov, prov];
}
NSString *final = [NSString stringWithFormat:#"(inpatient=%#) AND (dischargedate!=%#) AND (%#)", [NSNumber numberWithBool: self.inpatients], [NSNull null], preds];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:final]];
Yes you can, but you need to modify the format string slightly.
Instead of doing:
[preds appendFormat:#" (attending = %#)", prov];
You'd need to do:
[preds appendFormat:#" (attending = '%#')", prov];
Note the use of single-quotes around the %# modifier. That's how the predicate knows it's a constant value.
However, even if you go this route, you're still stuck using predicateWithFormat:, which you appear to want to avoid. You'll also likely have issues with how you're using NSNull in the format string.
I would recommend doing something more like this:
NSArray *provs = [self.providerCode componentsSeparatedByString:#"|"];
NSMutableArray *providerPredicates = [NSMutableArray array];
NSPredicate *template = [NSPredicate predicateWithFormat:#"attending = $prov OR admitting = $prov OR consulting CONTAINS[cd] $prov"];
for (NSString *prov in provs) {
NSDictionary *substitutions = [NSDictionary dictionaryWithObject:prov forKey:#"prov"];
NSPredicate *p = [template predicateWithSubstitutionVariables:substitutions];
[providerPredicates addObject:p];
}
NSPredicate *final = [NSPredicate predicateWithFormat:#"inpatient = 1 AND dischargedate != nil"];
if ([providerPredicates count] > 0) {
NSPredicate *providers = nil;
if ([providerPredicates count] > 1) {
providers = [NSCompoundPredicate orPredicateWithSubpredicates:providerPredicates];
} else {
providers = [providerPredicates objectAtIndex:0];
}
final = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:final, providers, nil]];
}
This is using a couple different neat things:
Predicate variables. You parse the format string #"attending = $prov OR admitting = $prov OR consulting CONTAINS[cd] $prov" once, and then simply substitute in new values for $prov each time you have a different provider
Constructing compound predicates. You use some class methods on NSCompoundPredicate to turn multiple predicates into a single, grouped OR or AND predicate.

Resources