Search code examples
iosobjective-ccore-datanspredicatensfetchrequest

Fetching only Core Data objects that share a value for a particular attribute


I have a Core Data object with several attributes, one of which is called Category. This is a string that could take on any value, nothing that I can guarantee any knowledge of up front. I want to run a fetch that will return a list of Category values that are shared across all stored elements whose Tag value matches a given value. I'm not sure how to best describe this, so here's an example:

ObjectNumber     Tag     Category
---------------------------------
01               AAA     Red
02               AAA     Green
03               AAA     Blue

04               BBB     Blue
05               BBB     Red
06               BBB     Yellow

07               CCC     Blue
08               CCC     Yellow
09               CCC     Red

I want the results of my fetch to be a list of Categories that are shared across all objects whose Tag is in the set {AAA, BBB, CCC}, which in this case, would return ["Red", "Blue"], because those are the only categories shared among any objects whose tag is in the given set.

I'm hoping to find something that's as fast as possible, because when this app is run on a real project, there will be tens of thousands of objects in the database.


Solution

  • Here is a code sample I just wrote up, that should solve your filtration question.

    I'm still learning the intricacies of NSFetchRequest and predicates, although it's not very complicated, after reading the documentation and experimenting.

    NSArray *setoftags = [NSArray arrayWithObjects: @"AAA", @"BBB", nil];
    NSFetchRequest * request;
    NSMutableSet *tagcategoriesset = nil;
    for (NSString *tag in setoftags) {
    
        request = [NSFetchRequest fetchRequestWithEntityName:@"ObjectWithCategories"];
        NSPredicate *tagPredicate = [NSPredicate predicateWithFormat:@"%K == %@", @"Tag", tag];
        request.predicate = tagPredicate;
        NSArray *objects;
        NSError *err;
        NSMutableSet *tagcategories = [[NSMutableSet alloc] initWithCapacity:1];
        if ( (objects = [self.managedObjectContext executeFetchRequest:request error:&err]) == nil) {
            // error
            NSLog(@"There was an error:\n%@", [err localizedDescription]);
        }
        else {
            // fetch request succeeded
    
    
            // Ignore any unused tags
            if ( [objects count] == 0) continue;
    
            // Collect the Category attribute into a mutable set
            for(NSManagedObject *obj in objects) {
                [tagcategories addObject: [(id)obj Category]];
            }
    
            // First assign, later intersect the tagcategories
            if (tagcategoriesset == nil) {
                tagcategoriesset = tagcategories;
            }
            else {
                [tagcategoriesset intersectSet: tagcategories];
            }
        }
    }
    
    NSArray *finalCategories = [tagcategoriesset allObjects];
    

    Here's another SO Q&A for further discussion: using NSPredicate with a set of answers