使用 NSPredicate 过滤对象

use NSPredicate to filter object

假设我有一个具有两个属性的对象数组:

// array of object
NSArray *objects
// object
NSString *primaryTag;
NSArray *secondaryTag;

因为我想要的是当 this 对象包含 givenTag 时,它可以传递给一个名为 results 的新数组;

这是我的代码:

NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"primaryTag == %@ || secondaryTag CONTAINS[c] %@", givenTag, givenTag];
results = [objects filteredArrayUsingPredicate:resultPredicate];

貌似primaryTag很好用,secondaryTag不行,谁能帮帮我。我不太熟悉 NSPredicate 过滤。提前致谢。

最有效的方法是使用 NSCompoundPredicate,如下所示:

NSArray *subPredicates = @[tag1, tag2, tag3];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

你的问题有点不清楚所以你可能还想:

andPredicateWithSubpredicates

具体取决于您要查找的结果集的性质。

在此处查看 Apple 文档:NSCompoundPredicate Docs

我看到了这个问题,

我的正常做法是使用 NSPredicate 两次,

这样我就可以跟踪每一步的结果: 选项 1:

NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:@"primaryTag == %@", givenTag]; results1 = [objects filteredArrayUsingPredicate:resultPredicate1];

NSPredicate *resultPredicate2 = [NSPredicate predicateWithFormat:@"secondaryTag CONTAINS[c] %@", givenTag]; finalResults = [results1 filteredArrayUsingPredicate:resultPredicate2];

选项 2: 使用 NSCompoundPredicate 复合多重过滤。您可以在 google 和 Whosebug 上轻松找到许多示例。

希望这会有所帮助, 谢谢

我实现了以下自定义 class:

@interface CustomObject : NSObject

@property (copy, nonatomic) NSString *primaryTag;
@property (strong, nonatomic) NSArray *secondaryTag;

@end

并覆盖 NSLog 语句的描述方法以打印我们理解的内容:

- (NSString *)description {
  return [NSString stringWithFormat:@"primaryTag: %@, secondaryTag: %@", _primaryTag, [_secondaryTag componentsJoinedByString:@", "]];
}

然后我从自定义 class 创建了一些对象并将它们添加到数组中:

NSMutableArray *objects = [NSMutableArray array];

CustomObject *obj1 = [CustomObject new];
obj1.primaryTag = @"stringToSearchFor";
obj1.secondaryTag = @[@"notTheStringToSearchFor", @"somethingElse"];
[objects addObject:obj1];

CustomObject *obj2 = [CustomObject new];
obj2.primaryTag = @"differentString";
obj2.secondaryTag = @[@"nothingWeAreLookingFor"];
[objects addObject:obj2];

CustomObject *obj3 = [CustomObject new];
obj3.primaryTag = @"anotherOne";
obj3.secondaryTag = @[@"whoCaresForThisString", @"stringToSearchFor"];
[objects addObject:obj3];

最后我创建了一个要搜索的字符串和谓词:

NSString *givenTag = @"stringToSearchFor";

NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"primaryTag == %@ || secondaryTag CONTAINS[c] %@", givenTag, givenTag];

当我注销结果时,我得到了正确的结果:

NSLog(@"%@", [objects filteredArrayUsingPredicate:resultPredicate]);

日志:

( "primaryTag: stringToSearchFor, secondaryTag: notTheStringToSearchFor, somethingElse", "primaryTag: anotherOne, secondaryTag: whoCaresForThisString, stringToSearchFor" )

这是obj1和obj3。正确的!如果它对您不起作用,那么您的代码一定有其他问题...

如果我对原问题的理解不正确,请告诉我,我会调整我的答案。

问题:您有一个具有 2 个属性的对象数组。一个是 primaryTag,这是一个字符串。第二个是secondaryTags的数组,是字符串的集合。您想要过滤 primaryTag 匹配或搜索字符串与 secondaryTag 之一匹配的所有对象。

答案 匹配字符串的正确方法是通过 MATCHESCONTAINS.

NSPredicate *pPredicate =
    [NSPredicate predicateWithFormat:@"%K CONTAINS[cd] %@",
                                     @"primaryTag", searchString];
NSPredicate *sPredicate =
    [NSPredicate
     predicateWithFormat:@"SUBQUERY(%K, $st, $st CONTAINS[cd] %@).@count > 0",
                         @"secondaryTags", searchString];
NSCompoundPredicate *searchPredicate = 
    [NSCompoundPredicate orPredicateWithSubPredicates:@[ pPredicate, sPredicate ]];

工作原理: 第一个谓词是直接匹配。您可以将 CONTAINS 替换为 MATCHES,如果这更适合您希望进行的比较。 [cd] 后缀表示不区分大小写和变音符号。在 searching/filtering 时包含那些是正常的,但同样,这取决于您。我没有在谓词格式字符串中嵌入 属性 名称,而是使用 %K 和一个替换参数。在生产代码中,该替换参数将是一个常量。

第二个谓词有点棘手。它使用 SUBQUERY() 来过滤 secondaryTags 数组,如果至少有一个辅助标记与搜索字符串匹配,则 returns 对象匹配。 SUBQUERY() 是一个有 3 个参数的函数。第一个是正在搜索的集合。第二个是一个临时变量,依次表示集合中的每个项目;它用于第三个参数。第三个参数是一个正则谓词。集合中与过滤器匹配的每个项目都包含在 SUBQUERY() 的输出中。最后,对匹配的二级标签进行计数(通过@count),如果计数大于零,则认为原始对象已匹配,因此将包含在过滤输出中。

最后,我们将这两个谓词合并为一个 searchPredicate,现在可以用来过滤您的对象数组。