NSPredicate用于匹配“NSDatabase中包含特定字符串的任何条目”。

6
我有一个类似下面的字典数组:
(
        {
            Black = "?";
            Date = "????.??.??";
            Result = "*";
            SourceDate = "2007.10.24";
            White = "交配模式#1";
        },
        {
            Black = "?";
            Date = "????.??.??";
            Result = "*";
            SourceDate = "2008.10.24";
            White = "关于本出版物";
        }
)
我想让用户能够在“White”和“Black”字段中搜索文本,或在任何字段中搜索。我已经有了一个用于特定字段搜索的NSPredicate:

    predicate = [NSPredicate 
                    predicateWithFormat:@"self.Black contains[cd] %@ or self.White contains[cd] %@",
                        searchText, searchText];
    [filteredGames addObjectsFromArray:[games filteredArrayUsingPredicate:predicate]];


我无法想出一个合适的谓词来返回其中任何一个对象匹配文本的字典。例如,我可以搜索“2007”,它将返回第一个字典但不会返回第二个字典。我尝试过“self.*”,但我并没有真正期望它能工作,并且还尝试了“ANY self.allValues”,我对它更有希望。但实际上,我事先并不知道键是什么,因此需要一些不太具体的东西。您有什么建议吗?
2个回答

16
如果所有的字典都有相同的键集,那么你可以做一些非常简单的事情:
NSArray *keys = ...; //the list of keys that all of the dictionaries contain
NSMutableArray *subpredicates = [NSMutableArray array];
for (NSString *key in keys) {
  NSPredicate *subpredicate = [NSPredicate predicateWithFormat:@"%K contains[cd] %@", key, searchText];
  [subpredicates addObject:subpredicate];
}
NSPredicate *filter = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];

然后,您可以使用filter来过滤您的NSArray(使用-filteredArrayUsingPredicate)。

另一方面,如果您有一个任意字典的数组,所有字典都有不同的键,那么您需要做一些更奇怪的事情:

NSPredicate *filter = [NSPredicate predicateWithFormat:@"SUBQUERY(FUNCTION(SELF, 'allKeys'), $k, SELF[$k] contains[cd] %@).@count > 0", searchText];

这段代码的作用:

  • FUNCTION(SELF, 'allKeys') - 执行SELF(一个NSDictionary)的-allKeys方法,返回一个包含字典中所有键的NSArray
  • SUBQUERY(allKeys, $k, SELF[$k] contains[cd] %@) - 遍历allKeys数组中的每一个元素,将每个元素存入变量$k中。对于每个元素,执行SELF[$k] contains %@。这基本上相当于执行:[theDictionary objectForKey:$k] contains[cd] %@。如果返回YES,则将$k聚合到一个新数组中。
  • SUBQUERY(...).@count > 0 - 找到所有与搜索文本匹配的键之后,检查是否有任何键匹配成功(即数组大小大于0)。如果有,则将整个字典添加到最终的过滤数组中。

我建议尽可能使用第一种方法。 SUBQUERYFUNCTION有点晦涩难懂,而第一种方法要容易理解得多。


这里有另一种方法,实际上你在问题中已经接近答案了。不要使用ANY SELF.allValues contains[cd] %@,而是使用ANY FUNCTION(SELF, 'allValues') contains[cd] %@。 这等同于我的SUBQUERY方法,但更简单。赞扬你想到使用ANY(我通常会忘记它的存在)。
编辑 SELF.allValues无法正常工作的原因是,它被解释为键路径,而-[NSDictionary valueForKey:]应该与-[NSDictionary objectForKey:]相同。关键是,如果你在键前加上@,那么它将转发到[super valueForKey:],这个方法会做你期望的事情。所以你可以这样做:
ANY SELF.@allValues contains[cd] %@

或者简单地说:

ANY @allValues contains[cd] %@

这样做是可行的(也是最好和最简单的方法)。


嗨,感谢您详细的回复 :-)不同字典之间的标题集可能并不相同,因此我无法使用您提到的第一种方法。其他方法看起来不错,但是我尝试使用了最后一种方法。据我所知,如果我搜索任何过滤器,都会出现异常。也就是说,如果我搜索所有字典中都存在的单个字母,则会起作用,其他任何内容都会失败,并且我会收到以下消息:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't use in/contains operator with collection 0 (not a collection)' - mcknut
@mcknut,你能在问题中提供有关字典数据的更多信息,并且给出一个具体的例子来说明它是如何引发异常的吗? - Dave DeLong
我正在进行编程,但可能做得不对,因为我经常需要使用 "!" 符号。我认为我应该切换到使用核心数据(Core Data)。在这种情况下,我将使 Keys & Values 存储到它们自己的实体中,并引用回父实体,然后搜索键和值实体以找到匹配项,我认为这是最好的方法。 - mcknut
@mcknut 搜索 ! 对我有效... 你能在问题中发布更多的代码吗? - Dave DeLong
抱歉,我已经重写了代码,现在使用Core Data!感谢您的帮助,无论如何都已将此解决方案标记为已接受。 - mcknut

0
如果你想在字典中匹配一个对象,可以通过使用for循环来实现,但这可能是一项耗时的任务。
for(int i=0; i< [array count]; i++)
{

    NSDictionary *dic = [array objectAtIndex:i];

    if([[dic objectForKey:@"Black"] isEqualToString:@"2007"])
    {
    //here is the match
    }
}

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