Using NSPredicate to filter based on multiple keys (NOT values for key)

So Over It picture So Over It · Jun 11, 2012 · Viewed 11k times · Source

I have the following NSArray containing NSDictionary(s):

NSArray *data = [[NSArray alloc] initWithObjects:
                 [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"bill", [NSNumber numberWithInt:2], @"joe", nil],
                 [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:3], @"bill", [NSNumber numberWithInt:4], @"joe", [NSNumber numberWithInt:5], @"jenny", nil],
                 [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:6], @"joe", [NSNumber numberWithInt:1], @"jenny", nil],
                 nil];

I am wanting to create a filtered NSArray that only contains objects where the NSDictionary matches multiple 'keys' using NSPredicate.

For example:

  • filter the array to only contain the NSDictionary objects that have keys "bill" and "joe" [desired result: new NSArray would contain the first two NSDictionary objects]
  • filter the array to only contain the NSDictionary objects that have keys "joe" and "jenny" [desired result: new NSArray would contain the last two NSDictionary objects]

Can anyone please explain the format of the NSPredicate to achieve this?

Edit: I can achieve a similar outcome to desired NSPredicate using:

NSMutableArray *filteredSet = [[NSMutableArray alloc] initWithCapacity:[data count]];
NSString *keySearch1 = [NSString stringWithString:@"bill"];
NSString *keySearch2 = [NSString stringWithString:@"joe"];

for (NSDictionary *currentDict in data){
    // objectForKey will return nil if a key doesn't exists.
    if ([currentDict objectForKey:keySearch1] && [currentDict objectForKey:keySearch2]){
        [filteredSet addObject:currentDict];
    }
}

NSLog(@"filteredSet: %@", filteredSet);

I'm imagining NSPredicate would be more elegant if one exists?

Answer

Alexander Ney picture Alexander Ney · Jun 11, 2012

they only way I know is to combine two conditions like "'value1' IN list AND 'value2' IN list"

self.@allKeys should return all the keys of the dictionary (self is each dictionary in your array). If you don't write it with the prefix @ then the dictionary will just look for a key that is "allKeys" instead of the method "- (NSArray*) allKeys"

The code:

NSArray* billAndJoe = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%@ IN self.@allKeys AND %@ IN self.@allKeys" , @"bill",@"joe" ]];


NSArray* joeAndJenny = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%@ IN self.@allKeys AND %@ IN self.@allKeys" , @"joe",@"jenny" ]]