95

I have an array of dictionaries.

I want to filter the array based on a key.

I tried this:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SPORT ==  %@)", @"Football"];

NSArray *filteredArray = [data filteredArrayUsingPredicate:predicate];

This doesn't work, I get no results. I think I'm doing something wrong. I know this is the method if "SPORT" was an ivar. I think it is probably different if it is a key.

I haven't been able to find an example however.

Thanks


Update

I added quotes around the string I am searching for.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SPORT ==  '%@')", @"Football"];

It still does not work.


Update 2

Solved it. I actually had to remove the single quotes, which seems to go against what the guide says.

My real problem is I had a nested array and I wasn't actually evaluating the dictionaries. Bone head move.

Corey Floyd
  • 25,929
  • 31
  • 126
  • 154
  • Just to mention that the predicate is case sensitive by default (use [c] to get unsensitive). – groumpf Sep 14 '10 at 09:48
  • possible duplicate of [ios sorting array of dictionaries by key of inner dictionary](http://stackoverflow.com/questions/9509138/ios-sorting-array-of-dictionaries-by-key-of-inner-dictionary) – QED Jul 25 '13 at 04:20

6 Answers6

152

It should work - as long as the data variable is actually an array containing a dictionary with the key SPORT

NSArray *data = [NSArray arrayWithObject:[NSMutableDictionary dictionaryWithObject:@"foo" forKey:@"BAR"]];    
NSArray *filtered = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(BAR == %@)", @"foo"]];

Filtered in this case contains the dictionary.

(the %@ does not have to be quoted, this is done when NSPredicate creates the object.)

suraken
  • 1,626
  • 1
  • 10
  • 4
  • Does NSPredicate work on the iPhone? It works great on the sim but running it on the iPhone gives an error that the NSPredicate class could not be found. – lostInTransit Sep 25 '09 at 04:28
  • NSPredicate is available since iOS 3.0. – zekel Jul 08 '12 at 01:00
  • 1
    to clarify for new coders like me, the key line of code is 'NSArray *filtered = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(BAR == %@)", @"foo"]];' where 'data' is the nsarray of nsdictionaries that is already in your code. (thank you suraken) – tmr Jan 28 '15 at 18:20
  • What if the key names are dynamic? – iPeter Jul 11 '17 at 03:51
27

I know it's old news but to add my two cents. By default I use the commands LIKE[cd] rather than just [c]. The [d] compares letters with accent symbols. This works especially well in my Warcraft App where people spell their name "Vòódòó" making it nearly impossible to search for their name in a tableview. The [d] strips their accent symbols during the predicate. So a predicate of @"name LIKE[CD] %@", object.name where object.name == @"voodoo" will return the object containing the name Vòódòó.

From the Apple documentation: like[cd] means “case- and diacritic-insensitive like.”) For a complete description of the string syntax and a list of all the operators available, see Predicate Format String Syntax.

Hemang
  • 26,840
  • 19
  • 119
  • 186
ChargedNeuron
  • 311
  • 3
  • 2
11
#import <Foundation/Foundation.h>
// clang -framework Foundation Siegfried.m 
    int
main() {
    NSArray *arr = @[
        @{@"1" : @"Fafner"},
        @{@"1" : @"Fasolt"}
    ];
    NSPredicate *p = [NSPredicate predicateWithFormat:
        @"SELF['1'] CONTAINS 'e'"];
    NSArray *res = [arr filteredArrayUsingPredicate:p];
    NSLog(@"Siegfried %@", res);
    return 0;
}
3

NSPredicate is only available in iPhone 3.0.

You won't notice that until try to run on device.

MathieuF
  • 3,130
  • 5
  • 31
  • 34
Rod
  • 1,975
  • 4
  • 19
  • 23
0

With Swift 3, when you want to filter an array of dictionaries with a predicate based on dictionary keys and values, you may choose one of the following patterns.


#1. Using NSPredicate init(format:arguments:) initializer

If you come from Objective-C, init(format:arguments:) offers a key-value coding style to evaluate your predicate.

Usage:

import Foundation

let array = [["key1": "value1", "key2": "value2"], ["key1": "value3"], ["key3": "value4"]]

let predicate = NSPredicate(format: "key1 == %@", "value1")
//let predicate = NSPredicate(format: "self['key1'] == %@", "value1") // also works
let filteredArray = array.filter(predicate.evaluate)

print(filteredArray) // prints: [["key2": "value2", "key1": "value1"]]

#2. Using NSPredicate init(block:) initializer

As an alternative if you prefer strongly typed APIs over stringly typed APIs, you can use init(block:) initializer.

Usage:

import Foundation

let array = [["key1": "value1", "key2": "value2"], ["key1": "value3"], ["key3": "value4"]]

let dictPredicate = NSPredicate(block: { (obj, _) in
    guard let dict = obj as? [String: String], let value = dict["key1"] else { return false }
    return value == "value1"
})

let filteredArray = array.filter(dictPredicate.evaluate)
print(filteredArray) // prints: [["key2": "value2", "key1": "value1"]]
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
-1

Looking at the NSPredicate reference, it looks like you need to surround your substitution character with quotes. For example, your current predicate reads: (SPORT == Football) You want it to read (SPORT == 'Football'), so your format string needs to be @"(SPORT == '%@')".

Martin Gordon
  • 36,329
  • 7
  • 58
  • 54
  • I thought thats what it said, too. Apparently the quotes aren't needed. I'm not sure exactly what the guide is saying quotes "should" be used for. – Corey Floyd Jun 06 '09 at 00:20