NSPredicate 匹配“NSDatabase 中的任何条目与包含字符串的值”

Posted

技术标签:

【中文标题】NSPredicate 匹配“NSDatabase 中的任何条目与包含字符串的值”【英文标题】:NSPredicate to match "any entry in an NSDatabase with value that contains a string" 【发布时间】:2011-04-06 16:11:59 【问题描述】:

我有一个字典数组,类似于以下内容:

( 黑色=“?”; 日期 = "????.??.??"; 结果 = "*"; SourceDate = "2007.10.24"; 白色 = "交配模式 #1"; , 黑色=“?”; 日期 = "????.??.??"; 结果 = "*"; SourceDate = "2008.10.24"; White = "关于本出版物"; )

我想让用户能够在“白色”和“黑色”字段内或任何字段内搜索文本。我有一个 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”,我对此更有希望。我实际上并不事先知道键是什么,因此需要一些不太具体的东西。

有什么建议吗?

【问题讨论】:

【参考方案1】:

如果所有字典都有相同的键集,那么你可以做一些非常简单的事情:

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') - 这将在 SELFNSDictionary)上执行 -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:] is supposed to be the same as -[NSDictionary objectForKey:]。这里要注意的是,如果您为密钥添加前缀@,那么它会转发到[super valueForKey:],这按照您的预期进行。所以你真的可以这样做:

ANY SELF.@allValues contains[cd] %@

或者简单地说:

ANY @allValues contains[cd] %@

这将起作用(并且是最好和最简单的方法)。

【讨论】:

嗨,感谢您的深入回复 :-) 字典之间的标题集不一定相同,因此我无法使用您提到的第一种方法。其他看起来不错,但我已经尝试了最后一个,据我所知,如果我搜索任何过滤器,我会遇到异常。即,如果我搜索所有字典中存在的单个字母,它可以工作,其他任何东西都失败了,我得到:***由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“不能在/中使用”包含具有集合 0 的运算符(不是集合)' @mcknut 您能否在有关字典中数据的问题中提供更多信息,以及它如何引发该异常的具体示例? 如果我搜索“!”则会出现异常这在字典中是相当罕见的。我实际上在想“我做错了”,我应该改用 Core Data。在那种情况下,我会将键和值存储在它们自己的实体中,并带有对父实体的引用,然后搜索键和值实体以找到匹配项,我认为这是最好的。 @mcknut 搜索 ! 对我有用...你能在问题中发布更多代码吗? 抱歉,我现在重写了代码以使用 Core Data!感谢您的帮助,已将此解决方案标记为已接受。【参考方案2】:

如果你想匹配字典中的一个对象,你可以使用 for 循环来做到这一点,但是 这可能是一项耗时的任务 -

for(int i=0; i< [array count]; i++)


    NSDictionary *dic = [array objectAtIndex:i];

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

【讨论】:

以上是关于NSPredicate 匹配“NSDatabase 中的任何条目与包含字符串的值”的主要内容,如果未能解决你的问题,请参考以下文章

NSPredicate 与字符串完全匹配

NSPredicate - 返回不精确的匹配

NSPredicate 精确匹配

将 NSPredicate 与字符串匹配

Cloudkit 的 NSPredicate 匹配字符串

NSPredicate匹配字符串