Suppose I have one entity 'Person' in core data.
Now i want to search all persons. Either firstname beginning match or lastname beginning match.
For eg :
1) Amit Gupta
2) Ajay Gulati
3) Gunjan Aggarwal
Searching for 'Gu' shows names that match firstname first, then those that match lastname
therefore result is :
Gunjan Aggarwal
Ajay Gulati
Amit Gupta
One option : Fetch all objects , store them in array and then sort.
But what if the search results are very big in number
Second option : Use NSFetchedResultsController
This will fetch all matching but not in the required manner(firstname before lastname).
Cant use sort descriptors as it is not sorting on any key, but upon matching.
Can anybody help ?
EDIT :
First name and lastname are two different attributes of 'Person' entity.
Either Firstname matches or Lastname matches.
I want results with 'Firstname' match before than results with 'Lastname' match.
If you use sort descriptor, which 'Key' or 'attribute' will you mention ???
Try to set Sort Descriptors before fetching:
NSSortDescriptor * firstNameDescriptor;
firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor * lastNameDescriptor;
lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
[firstNameDescriptor release];
[lastNameDescriptor release];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:firstNameDescriptor, lastNameDescriptor, nil]];
then fetch your desired persons.
After you got a sorted result, in order to match your searching, you might need to resort it:
- (NSComparisonResult)compare:(id)anotherOne {
// if the first name matches the searching key word, return NSOrderedAscending;
// else return [self.firstName localizedCaseInsensitiveCompare:anotherOne.firstName];
}
then just apply this to your search result array with sortedArrayUsingSelector: method. No testing code available, but I think you can figure it out by yourself then. ;)
Use a transient property sortName in your Core Data object, make it compare the firstName and lastName properties and just return whichever comes first alphabetically.
Now just use this property as your sortDescriptor and you should get the result you are after.
Something like this...
- (NSString *)sortName
{
[self willAccessValueForKey:#"sortName"];
NSString *string = nil;
if ([self.firstName compare:self.lastName] == NSOrderedAscending) {
string = self.firstName;
} else {
string = self.lasttName;
}
[self didAccessValueForKey:#"sortName"];
return string;
}
Use two fetch requests. When you're providing data to the view, display the results of the first fetch request, followed by the results of the second.
Related
I have a data model setup with three entities, Course, Student, TestScores.
They are linked in too-many relationships like this:
Course <---->> Student <---->> TestScores
So a Course would have several Students, who in turn could have several TestScores (or no test scores)
The Course entity has a Name attribute. TestScores is a simple entity which just contains a testScore int attribute.
I want to be able to get an array of Students who have at least one textScore of 100, ordered by Course name. Is this possible with NSPredicate?
I think you could have your predicate as
ANY testScores.score == 100
Then put it all together in a fetch request:
NSFetchRequest *req = [NSFetchRequest fetchRequestForEntityNamed:#"Student"];
req.predicate = [NSPredicate predicateWithFormat:#"ANY testScores.score == 100"];
req.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"course.name" ascending:YES]];
I'm new to core data and having some difficulty finding any information about sorting and only fetching non duplicates of a specified attribute.
I have a list of locations and they all have different addresses but some have the same city. i would like to fetch all the cities excluding duplicate cities and in alphabetical order.
Would the best way be to have 2 attributes, one for city and another for locationDetails. in the city attribute it will just have a list of cities with no duplicates and when selecting a city it will fetch all the locationDetails for that city attribute?
Thanks,
Yes the best way will be to have a separate attribute for city. And you don't need to structure your entity as unique. You can take care of sorting and fetching unique in your fetch request.
NSFetchRequest *request=[[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"<your entity name>" inManagedObjectContext:context]];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:[NSArray arrayWithObject:#"City"]];
request.sortDescriptors=[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"City" ascending:YES]];
[request setReturnsDistinctResults:YES];
Note: This request returns an array of dictionaries with City as its key. Don't forget to release the NSFetchRequest after executing the fetch.
EDIT
When the user clicks on a city, store it in some variable (cityName). Now execute another fetch request like:
request.predicate=[NSPredicate predicateWithFormat:#"City = %#",cityName];
You should do this with the same entity. This will fetch only the objects whose city name has been selected. Now, you can either do the fetch in 2 ways:
Fetch the entire entities and then dispay using the format
entityName.Location. In this case you will get an NSArray of
objects
Set the properties to fetch like:
[request setPropertiesToFetch:[NSArray arrayWithObject:#"Location"]];
In the second case, it returns a NSDictionary ( Don't forget to set Result Type as NSDictionaryResultType as before ).
You should structure your Core Data model so that you have a City entity for each unique city and a Location entity with a one-to-one relationship to the relevant City entity (and a one-to-many reverse relationship). The city name itself should not be part of the Location entity. When structured this way, you simply fetch all City entities.
As for sorting, you should add a NSSortDescriptor to your NSFetchRequest. For example:
NSSortDescriptor *sorter;
sorter = [[NSSortDescriptor alloc] initWithKey: #"name" ascending: YES];
[fetchRequest setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]];
For a relationship like this:
TagGroups<---->>Tags<<---->>Object
An Object has tags, tags can be grouped by tagGroups.
I have an object, and I want to know all of the TagGroups its Tags belong to.
To construct he predicate, I first tried the following format string:
(SELF is a TagGroup)
NSPredicate* p = [NSPredicate predicateWithFormat:#"%# IN SELF.tags.objects" , object];
This fails because sets are not traversed ala Key-ValueCoding.
After some research I have found several questions explaining SUBQUERY
Core Data, try to use NSPredicate to filter a toMany relationship set but get the "to-many key not allowed here" error
Core data to-many NSPredicate with AND
These seem to be part of the solution, but unlike these questions I am not testing for a value like "tag.name", but membership within the collection.
So with that in mind I tried this:
NSPredicate* p = [NSPredicate predicateWithFormat:#"%# IN SUBQUERY(SELF.tags, $eachTag,$eachTag.object)" , object];
Which fails to parse (I tried a few other variants unsuccessfully as well)
Any ideas on how to construct this format string properly?
Update:
Also tried it from the other direction:
NSPredicate* p = [NSPredicate predicateWithFormat:#"ALL SUBQUERY(%#.tags,$eachTag,$eachTag.tagGroup)" , anObject];
If you "have an object" i.e. you have a particular managedObject whose entity is Object then you don't need a predicate. You just need to walk the relationship keypath.
Here is an example implemented with dictionaries by it works the same way with a managed object.
NSDictionary *tagGroup1=[NSDictionary dictionaryWithObjectsAndKeys:#"tagGroupOne",#"name",#"tagGroup1",#"theValue",nil];
NSDictionary *tagGroup2=[NSDictionary dictionaryWithObjectsAndKeys:#"tagGroupTwo",#"name",#"tagGroup2",#"theValue",nil];
NSDictionary *tag1=[NSDictionary dictionaryWithObject:tagGroup1 forKey:#"tagGroup"];
NSDictionary *tag2=[NSDictionary dictionaryWithObject:tagGroup2 forKey:#"tagGroup"];
NSSet *tags=[NSSet setWithObjects:tag1,tag2,nil];
NSDictionary *objD=[NSDictionary dictionaryWithObjectsAndKeys:tags,#"tags",nil];
NSLog(#"tagGroup names=%#",[objD valueForKeyPath:#"tags.tagGroup.name"]);
NSLog(#"tagGroup objects=%#",[objD valueForKeyPath:#"tags.tagGroup"]);
... which outputs:
tagGroup names={(
tagGroupTwo,
tagGroupOne
)}
tagGroup objects={(
{
name = tagGroupTwo;
theValue = tagGroup2;
},
{
name = tagGroupOne;
theValue = tagGroup1;
}
)}
So, really all you need is a line like:
NSSet *tagGroups=[anInstanceOfObject valueForKeyPath:#"tags.tagGroup"];
That's the power of key-value coding.
You would only need a subquery if you were trying to fetch Objects that had a relationship to a TagGroup with a particular attribute value.
Another way to word this might be...
NSPredicate "state.country == 'United States'"
is like
SQL "Select * from state where state.country = 'United States'
so how do I do this as a predicate?
SQL "Select state.name from state where state.county = 'United States'"
ORIGINAL POST:
I have a dictionary of arrays of dictionaries that looks like this:
lists
states
state
country
country
country
I have code to filter states by country. However, I'm betting there is a cleaner way.
NSArray *states = [lists valueForKey:#"states"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"countryname == %#", selectedCountry];
states = [states filteredArrayUsingPredicate:predicate];
states = [states valueForKeyPath:#"state"];
Ideas?
You've almost got it:
NSDictionary * lists = ...;
This is your original dictionaryl
NSArray *states = [lists objectForKey:#"states"];
This is to retrieve the array of states from your dictionary. Presumably these are actual "State" objects (ie, you made a class called State)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"countryname == %#", selectedCountry];
This creates a predicate that will compare the results of -[State countryname] to the value referenced by the selectedCountry variable. Alternatively, if the states array contains dictionaries, this will compare the results of -[NSDictionary objectForKey:#"countryname"] to the value referenced by selectedCountry.
states = [states filteredArrayUsingPredicate:predicate];
This will retrieve all the states from the array where [[aState countryname] isEqual:selectedCountry]
states = [states valueForKeyPath:#"name"];
This will create a new array that contains the name of each state. This new array will be in the same order as your filtered states array.
I'm only using NSArray and not NSDictionary so I don't know how useful this is but I'm using this:
matchingTour = [self.tours filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"tourCode == %#", tourCode]];
where self.tours is an NSArray of Tour objects. A Tour object has a property of NSString *tourCode. matchingTour is of type NSArray.
I was amazed when I read about this, tried it and it worked first time and I got back an array containing just the one tour!
In your 'tree' you don't show any property name 'countryname' - just wondering.
Hey,
I have an Array of NSDictionaries (e.g. [tempArray addObject:[[[NSMutableDictionary alloc] initWithObjects:someArray forKeys:keys] autorelease]]; ) and when I try to search through it with NSPredicate it keeps giving me 'No Results'. (TempArray becomes self.patientList)
I have no compile-time warnings or errors, and if I NSLog([self.filteredArray count]); then it returns 0 as well.
Here is my relevant code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(first contains[cd] %#)", self.searchBar.text];
self.filteredArray = [self.patientList filteredArrayUsingPredicate:predicate];
return [self.filteredArray count];
self.patientList and self.filteredArray are types NSMutableArray and NSArray respectively (changing self.filteredArray to NSMutableArray doesn't help).
I have tried using == instead of contains as well. Using #"SELF contains[cd] %#" only returns an item when the full item is typed (i.e. if the key 'name' is #"Bob" then if I type in Bob it will display it, but not when I type Bo or ob).
I'm really stumped on this one.
Thanks in advance!
Apparently first is a reserved word when using predicates. From Apple's documentation:
The following words are reserved:
AND, OR, IN, NOT, ALL, ANY, SOME,
NONE, LIKE, CASEINSENSITIVE, CI,
MATCHES, CONTAINS, BEGINSWITH,
ENDSWITH, BETWEEN, NULL, NIL, SELF,
TRUE, YES, FALSE, NO, FIRST, LAST,
SIZE, ANYKEY, SUBQUERY, CAST,
TRUEPREDICATE, FALSEPREDICATE
Just change your key to something else, firstName perhaps, and it will work.
You can use "like" instead:
first like[c] *%#*
At first glance, it seems correct. So then: do your dictionaries actually have a key called first? And if they do, does it match the case exactly?