ammar shahid ammar shahid - 20 days ago 5
iOS Question

Using NSPredicate to filter a value in NSDictionary and return the keys array

I've a dictionary with a string value and an int key.

i.e.

{1,abc}

{2,bcd}

{3,cde}

I'm filtering it using NSPredicate as follows

NSMutableDictionary *tableDataSearch;
NSArray *searchResults;

NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"self contains[cd] %@", searchText];

searchResults = [[tableDataSearch allValues] filteredArrayUsingPredicate:resultPredicate];


it is returning me the array of values which contain the specific word. I want all keys to be returned in the array and search in values like it is searching right now.
any help ?

Answer

The following code filters an NSDictionary based on a substring search on its values:

NSDictionary *data = @{@1 : @"abc",
                       @2 : @"bcd",
                       @3 : @"def"};

NSString *searchText = @"bc";

NSMutableDictionary *filteredData = [NSMutableDictionary new];

[data enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
    NSRange range = [obj rangeOfString:searchText
                                options:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch];
    if (range.location != NSNotFound)
    {
        [filteredData setObject:obj forKey:key];
    }
}];
NSArray *keys = [filteredData allKeys];

If this feels a bit cumbersome, BlocksKit provides some useful extensions to NSDictionary:

NSDictionary *filteredData = [data bk_select:^BOOL(id key, NSString *obj) {
    NSRange range = [obj rangeOfString:searchText
                               options:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch];
    return range.location != NSNotFound;
}];

And if you prefer to use NSPredicate, you can replace range.location != NSNotFound with:

[resultPredicate evaluateWithObject:obj];