Filter an NSDictionary of Arrays using NSPredicate
I have a plist that I have defined as a Dictionary that contains many arrays. I would like to pull out of that dictionary all arrays that have their first "Item 0" (Default naming convention) that matches a given string.
I tried something similar to Filtering an NSAr开发者_Python百科ray, but I am getting a regex error and the console spews out the contents of the first array in the dictionary.
Also, would I need to instantiate my NSDictionary as an NSMutableDictionary since I will be performing a filter process on it? Is it better form to copy the NSDictionary read in from the plist to a mustable one and then do the work on it?
It's possible, but in a roundabout way. I recommend that you consider using a different approach.
That being said, here's how you'd do it:
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:
[NSArray arrayWithObjects:@"a", @"b", @"c", nil], @"a",
[NSArray arrayWithObjects:@"b", @"c", @"a", nil], @"b",
[NSArray arrayWithObjects:@"c", @"a", @"b", nil], @"c",
[NSArray arrayWithObjects:@"a", @"b", @"c", nil], @"d",
nil];
NSPredicate *p = [NSPredicate predicateWithFormat:@"%@[SELF][0] == 'a'", d];
NSLog(@"%@", p);
NSArray *keys = [d allKeys];
NSArray *filteredKeys = [keys filteredArrayUsingPredicate:p];
NSLog(@"%@", filteredKeys);
NSDictionary *matchingDictionary = [d dictionaryWithValuesForKeys:filteredKeys];
NSLog(@"%@", matchingDictionary);
Here's what's going on:
We've got our source dictionary, d
. This is a dictionary where a letter (a
, b
, c
, or d
) is keyed to an array. We're going to find all the keys that correspond to an array where the first element is a
.
Our predicate is:
[NSPredicate predicateWithFormat:@"%@[SELF][0] == 'a'", d];
Here we're using the indexing operator on our source dictionary. SELF
, when this predicate is evaluated, will be one of the keys of d
. So %@[SELF]
will return an array. We then take the 0th element of that array and compare it to the string a
. If it matches, then this returns YES
.
We then grab all the keys in the dictionary and filter them using this predicate. This means that the resulting array will contain only the keys where the first element of the corresponding array is a
.
Once we have the matching keys, we extract a "sub dictionary" from our source dictionary to get our final, reduced dictionary.
In the test above, matchingDictionary
has two keys (a
and d
), which are both keyed to an array containing (a, b, c)
.
If you want an NSArray
instead that contains the matching arrays, you'd use -[NSDictionary objectsForKeys:notFoundMarker:]
instead.
However, this is an awkward solution, and I still think you should reorganize your data.
I'm pretty sure you can't use NSPredicate here (although if you wanted to use NSPredicate on the arrays within the NSDictionary you could). However, there's a perfectly acceptable alternative, as illustrated by the following example:
NSMutableArray* filteredArrays = [[NSMutableArray alloc] init];
for (NSString* currentKey in myDictionaryOfArrays)
{
NSArray* currentArray = [myDictionaryOfArrays objectForKey:currentKey];
NSString* objectAtFirstIndex = [currentArray objectAtIndex:0];
if ([objectAtFirstIndex isEqualTo:mySearchString]);
{
[filteredArrays addObject:currentArray];
}
}
By the way, the fast enumuration ("for each") statement should be used because you can get a performance increase from it.
What you are left with is a mutable array of arrays that match your search criteria. If you want to be a bit more tidy (for example if this code forms a method) you could convert this to a non-mutable array (unless of course you want to continue making changes).
NSArray* filteredResults = [NSArray arrayWithArray:filteredArrays];
精彩评论