I have a lot of people NSManagedObjects that I need filtering and was hoping to do it within the initial fetch instead of filtering the array afterwards. I've used selectors in predicates before, but never when fetching NSManagedObjects, for example I have all my employees and then i use this predicate on the NSArray...
[NSPredicate predicateWithFormat:#"SELF isKindOfClass:%#", [Boss class]]
...but now I want to do a bit more math based on different attributes of my objects. I thought I could do something like...
[NSPredicate predicateWithFormat:#"SELF bonusIsAffordable:%f", howMuchMoneyTheCompanyHas];
..where bonusIsAffordable: is a method of my Employee class and would calculate whether I can afford to pay them a bonus. But I get an error...
Unknown/unsupported comparison predicate operator type cocoa
Any ideas what I'm screwing up?
This gets a whole lot easier with Blocks:
NSPredicate *bossPred = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:[Boss class]];
}];
You can execute arbitrary code in an NSPredicate only when qualifying objects in memory. In the case of a SQLite-backed NSPersistentStore, the NSPredicate is compiled to SQL and executed on the SQLite query engine. Since SQLite has no knowlege of Objective-C, nor are any objects instantiated, there's no way to execute arbitrary code.
For in-memory queries (against a collection or an in-memory or atomic Core Data store), have a look at NSExpression, particular +[NSExpression expressionForFunction:selectorName:arguments:] and +[NSExpression expressionForBlock:arguments:]. Given such an expression, you can build an NSPredicate programatically.
Your predicate string doesn't tell the predicate object what to do. The method presumably returns a boolean but the predicate doesn't know what to compare that to. You might as well have given it a predicate string of "TRUE" and expected it to know what to do with it.
Try:
[NSPredicate predicateWithFormat:#"(SELF bonusIsAffordable:%f)==YES", howMuchMoneyTheCompanyHas];
Related
I have an array which consist of core data class objects. I want to use NSPredicate (for suggestive search) to filter out a particular property of that class.
Requirement
I am having a core data classes: AlbumDetails (properties- album name, albumType,etc)
i want to access AlbumDetails.AlbumName property using NSPredicate for my search bar text.
I currently know using predicate for simple array or dictionary structures, but how can i use predicate for a property of a particular class.
You can use something like
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"albumName contains[cd] %#", searchString];
Where albumName is the property of your class. contains will help you to get all the albums whose name contains the searchString
I am trying to filter a mutable array of objects using NSPredicate and am having trouble access the level that contains the property I would like to filter on.
To give a simplified example consisting of the similar custom objects.
Grandparent
Parent
Child
I have an NSMutableArray of Grandparents and I would like to find all the Grandparent Objects that have GrandChildren of age 10.
Therefore the grandchildren are two levels deep from the root. Child has an age property amongst other things.
ie. Grandparents has an array property Parents and Parents has an array property Children and Children has an integer property age.
The following NSPredicate has returned no results. "SELF.parents.children.age == 10".
I realise that as these are nested collections this predicate is likely the wrong way to go about it but I am stuck as to how to access that level. Perhaps via a Subquery or Collection Operator but I cannot work it out.
One thing to keep in mind is that I obviously still want GrandParents that have multiple Grandchildren of different ages, one of which is aged 10.
The "obvious" solution would be the predicate:
"ANY parents.children.age == 10"
However, the "ANY" operator does not work with nested to-many relationships. Therefore, you need a SUBQUERY:
NSArray *grandParents = your array of GrandParent objects;
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"SUBQUERY(parents, $p, ANY $p.children.age == 10).#count > 0"];
NSArray *filtered = [grandParents filteredArrayUsingPredicate:predicate];
Remarks:
Using SELF in the predicate is not necessary. filteredArrayUsingPredicate applies the
predicate to each GrandParent object in the array.
The usage of SUBQUERY in predicates seems to be poorly documented. There is one example in the NSExpression class reference. See also Quick Explanation of SUBQUERY in NSPredicate Expression.
In this case, the predicate inside SUBQUERY is applied to each Parent of a single GrandParent. The SUBQUERY returns the parents that have any child aged 10.
So "SUBQUERY(...).#count > 0" evaluates to TRUE if the grandparent has at least one parent that has any child aged 10.
ADDED: I just found out that it can actually be done without SUBQUERY:
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"ANY parents.#unionOfArrays.children.age == 10"];
works and gives the desired result. (It could be less effective than the SUBQUERY, but I did not test that.)
As an alternative to Martin R's answer, you might consider using a block predicate instead. Something like this:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) {
GrandParent *grandparent = evaluatedObject;
for (Parent *parent in grandparent.parents)
for (Child *child in parent.children)
if (child.age == 10)
return YES
return NO;
}];
Assuming GrandParent, Parent and Child are the appropriate class names of the various objects.
Personally I prefer this form, because I always feel with a string predicate that I'm mixing languages in the code, which I think makes it less readable. The choice is obviously up to you though.
Update: Having re-read the question, I now realise that the condition was more complex than I originally thought. I've updated my answer to loop over the parents and children, but Martin R's answer is now clearly a lot simpler. Still this is a possible solution to consider.
More for my interest than anything else.
If you have an Class defined like so...
MyClass
-------
NSString *name
And you put a lot of them into an array (or mutable array). Then you can use a predicate like this...
[NSPredicate predicateWithFormat:#"name = %#", someValue];
to filter the array so that it only contains objects whose names are the value given.
Or sort descriptor like so...
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
to sort the array by the name field in ascending order.
My question is, if you have an array of strings (or of NSNumbers) can you use similar "format" predicates?
Say for instance you had the array...
#[#"Cat", #"Bat", #"Dog", #"Cow"];
Could you use a "predicateWithFormat" to filter this array of a "sortDescriptorWithKey" to sort it?
I know you can use blocks but just wondering if this is possible?
Sure, you can filter an array of strings, or anything else, with predicateWithFormat:. As for sorting, you would use sortedArrayUsingSelector:, and use whichever selector you want (compare:, caseInsensitiveCompare:, etc.). There are no keys in a simple array, so you couldn't use sortDescriptorWithKey.
A string does not have any keys to use your predicates on or to sort by. You can find every other possible way to sort an array in the apple docs.
is the syntax for the line of code below correct?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type == %#",selectedAnimalType];
I want the 'selectedAnimalType' string to be used in a search to display the the user selected.
I ran an NSLog statement for the %# object and it returned what I wanted
NSLog(#"%#",selectedAnimalType);
thanks for any help.
Whether it works depends on what class type and selectedBirdType are. If they are both objects like NSStrings or NSNumbers it will work fine. If not you may have problems.
To see exactly what the predicate is just log the predicate object itself. It will print out the exact, populated i.e. with variables substituted, predicate so you can see exactly what is going on.
I was wondering if I could select objects based on a predicate with an array... for example
Code:
[NSPredicate predicateWithFormat:#"id=%#", arrayOfID];
Will it work? If no, how can I do it?
Best
The correct predicate would be
[NSPredicate predicateWithFormat:#"id IN %#", arrayOfID];
Assuming that arrayOfId contains objects of the same type as id (e.g. NSNumbers or NSStrings).
Yes, of course you can do it.
Sounds like you need a bit of grounding in Core Data. I found the following tutorial really useful in getting me off the ground with Core Data:
http://iphoneinaction.manning.com/iphone_in_action/2009/08/core-data-part-1-an-introduction.html