Based on the data mode开发者_运维技巧l below
And based on user input I create a NSSet of managedObjects of entity Tag called selectedTags.
My problem:
[NSPredicate predicateWithFormat:@"ANY entryTags IN %@", selectedTags];
... this will return any Entry with at least one entryTag that is in the selectedTags set.
I want something along the lines of:
[NSPredicate predicateWithFormat:@"ALL entryTags IN %@", selectedTags];
... notice the only change is the "ANY" to "ALL". This illustrates what I want, but does not work.
To formulate the outcome I expect:
I'm looking for a solution that will return only Entries who's entryTags are all in the selectedTags list (but at the same time, if possible, not necessarily the other way around).
To further illustrate:
(tag)Mom
(tag)Dad (tag)Gifts(entry)she is a she.....(tag)mom
(entry)he is a he........(tag)dad (entry)gifts for mom...(tags:)mom, gifts (entry)gifts for dad.....(tags:)dad, giftsIf selectedTags contains "mom" and "gifts", then the entry "gifts for dad" will show up, since it has the tag "gifts". I'd rather have it not show :)
This is the definite answer so far:
[NSPredicate predicateWithFormat:@"SUBQUERY(entryTags, $tag, $tag IN %@).@count = %d", selectedTags, [selectedTags count]];
B-E-A-U-T-I-F-U-L.
Thanks to Dave DeLong.
How about using a compound predicate? As I understand you want to return all Entries that match a list of tags not just any of them. One approach would be to create a predicate for each tag, then AND them together using a compound predicate.
NSMutableArray *predicates = [[NSMutableArray alloc] init];
for (Tag *tag in selectedTags) {
[predicates addObject:[NSPredicate predicateWithFormat:@"ANY entryTags.tagName MATCHES %@", tag.tagName]];
}
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
This should achieve want you want. Then just set this predicate on your request.
You can't do what you want with a predicate.
The ANY
and ALL
operators apply to the entity being tested (in this case Entry
) and not the contents of the collection (selectedTags
). Either operator will return an Entry
object that matches any single element of the collection. The ANY
operator will return the first match it finds while the ALL
operator will return all matches. In neither case will they return an entry that matches every element in the provided collection.
(It also looks like you are trying to use actual Tag
objects in selectedTags
. That will most likely not work either because object compares on classes without dedicated comparison methods usually fail. It is also slow. You need to compare attributes in predicates.)
Since you already have the Tag
objects you want, to find the candidate related Entity
objects, you just have to walk the Tag.taggedEntries
relationship. Then you have to find the intersection of all the sets of Entity
object to find only those Entity
objects that are related to every selected Tag
bject. Since there isn't an intersect collections operator, you need a loop.
if ([selectedEntries count]>=2) {
NSMutableSet *intersectEntries=[[NSMutableSet alloc] initWithCapacity:1];
for (int i=1; i<[selectedTags count]; i++) {
if ([intersectEntries count]==0) {
[intersectEntries unionSet:[[selectedEntries objectAtIndex:(i-1)] valueForKey:@"taggedEntries"]];
}
[intersectEntries intersectSet:[[selectedEntries objectAtIndex:i] valueForKey:@"taggedEntries"]];
}
}
(Note: I didn't test this but it should work.)
Now intersectEntries
should contain only those Entry
objects that are related to every selected tag.
I realized I could give something back here for the guidance that I have previously gotten. By using the code TechZen supplied I was able to come up with the following -- and for me highly valued -- piece of code:
- (NSArray *)unionSetOfObjectsForObjects:(NSArray *)objects {
NSMutableSet *unionSetOfObjects = [NSMutableSet set];
if (objects.count)
[unionSetOfObjects unionSet:[[objects objectAtIndex:0] valueForKey:@"key"]];
//unionSetOfObjects = [[objects objectAtIndex:0] valueForKey:@"key"];
if (objects.count > 1)
for (id object in objects)
[unionSetOfObjects intersectSet:[object valueForKey:@"key"]];
return unionSetOfObjects.allObjects;
}
If it is not immediately obvious what this code does:
It collects all the values (in my case objects) for the key key
on all of the objects provided in the objects
array.
This code just... tastes good, doesn't it?
The simplest way to do this:
[NSPredicate predicateWithFormat:@"entryTags IN %@", selectedTags];
You don't need the ALL clause. It's also documented here:
Predicate Programming guide
And as you can see in this post the user does it successfully (read the comments to the original question)
NSPredicate iPhone 3.2 SDK Core Data “IN clause”...
精彩评论