Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 352
So Over It Avatar asked Jun 11 '12 13:06

So Over It


2 Answers

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" ]]
like image 124
Alexander Ney Avatar answered Nov 15 '22 19:11

Alexander Ney


Since a dictionary just returns nil if you ask for a value of a non-existing key, it is enough to specify that the value should be non-nil. A format like the following should cover your first case:

[NSPredicate predicateWithFormat: @"%K != nil AND %K != nil", @"bill", @"joe"]

The second case, with "joe" and "jenny" follows a similar pattern, of course.

like image 42
Monolo Avatar answered Nov 15 '22 20:11

Monolo