使用NSPredicate根据NSDictionary键过滤NSArray

95
我有一个字典数组。
我想根据一个键过滤数组。
我尝试了这个:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SPORT ==  %@)", @"Football"];

NSArray *filteredArray = [data filteredArrayUsingPredicate:predicate];

这个方法不起作用,我没有得到任何结果。我认为我做错了什么。如果 "SPORT" 是一个 ivar,我知道这是正确的方法。但如果它是一个 key,那么方法可能不同。

然而,我还没有找到一个例子。

谢谢


更新

我在我要搜索的字符串周围添加了引号。

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

它仍然不起作用。


更新2

问题已解决。实际上,我需要删除单引号,这似乎与指南的要求相反。

我的真正问题是我有一个嵌套的数组,而我实际上没有评估字典。这是个愚蠢的错误。


只是提一下,默认情况下谓词区分大小写(使用[c]来获取不区分大小写)。 - groumpf
6个回答

152

只要data变量实际上是一个包含键SPORT的字典数组,它就可以工作。

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

在这种情况下,Filtered 包含一个字典。

(%@ 不需要加引号,这是 NSPredicate 创建对象时自动完成的。)


NSPredicate在iPhone上工作吗?它在模拟器上运行得很好,但在iPhone上运行时会出现一个错误,指出找不到NSPredicate类。 - lostInTransit
NSPredicate自iOS 3.0起可用。 - zekel
1
为了让像我这样的新手程序员更加清晰,关键代码行是“NSArray *filtered = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(BAR == %@)", @"foo"]];”,其中“data”是已经存在于您的代码中的NSDictionary数组。(感谢suraken) - tmr
如果键名是动态的呢? - iPeter

27

虽然这已经是老话题了,但我想加入讨论。默认情况下,我使用命令 LIKE[cd] 而不是仅仅使用 [c]。其中 [d] 可以比较带有音符的字母。在我的 Warcraft 应用程序中,人们会将自己的名字拼成 "Vòódòó" 等等,这使得在表视图中搜索他们的名字几乎是不可能的。而 [d] 在谓词中去掉他们的音符。所以,一个谓词如@"name LIKE[CD] %@", object.name,其中 object.name == @"voodoo" 将返回包含名称 Vòódòó 的对象。

根据苹果文档: like[cd] 意味着“不区分大小写和音符的 like”。有关字符串语法的完整描述和所有可用运算符的列表,请参阅谓词格式字符串语法


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 只在 iPhone 3.0 及以上版本可用。

只有在尝试在设备上运行时才会注意到这一点。


0

使用Swift 3时,如果你想根据字典的键和值来使用谓词过滤字典数组,可以选择以下模式之一。


#1. 使用NSPredicateinit(format:arguments:)初始化器

如果您来自Objective-C,init(format:arguments:)提供了一种键值编码风格来评估谓词。

用法:

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. 使用 NSPredicate init(block:) 初始化器

如果你喜欢强类型的 API 而不是字符串类型的 API,作为替代方案,你可以使用 init(block:) 初始化器。

用法:

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"]]

-1

看起来你需要在替换字符周围加上引号,可以参考NSPredicate reference。例如,你当前的谓词是:(SPORT == Football),你需要将其改为(SPORT == 'Football'),因此你的格式字符串应该是@"(SPORT == '%@')"


我也以为是这样说的。显然,引号不是必需的。我不确定指南中引号应该用于什么。 - Corey Floyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接