7

我正在尝试使用 NSPredicate 过滤一个可变的对象数组,并且无法访问包含我要过滤的属性的级别。

给出一个由类似的自定义对象组成的简化示例。

  • 祖父母
  • 家长
  • 孩子

我有一个 NSMutableArray 的祖父母,我想找到所有具有 10 岁孙子的祖父对象。因此,孙子从根深两层。除其他事项外,儿童具有年龄属性。

IE。祖父母有一个数组属性父母和父母有一个数组属性儿童和儿童有一个整数属性年龄。

以下 NSPredicate 未返回任何结果。"SELF.parents.children.age == 10".

我意识到,由于这些是嵌套集合,因此该谓词可能是错误的方法,但我对如何访问该级别感到困惑。也许通过子查询或集合运算符,但我无法解决。

要记住的一件事是,我显然仍然希望祖父母有多个不同年龄的孙子,其中一个是 10 岁。

4

2 回答 2

16

“明显”的解决方案将是谓词:

"ANY parents.children.age == 10"

但是,“ANY”运算符不适用于嵌套的一对多关系。因此,您需要一个子查询:

NSArray *grandParents = your array of GrandParent objects;
NSPredicate *predicate = [NSPredicate
   predicateWithFormat:@"SUBQUERY(parents, $p, ANY $p.children.age == 10).@count > 0"];
NSArray *filtered = [grandParents filteredArrayUsingPredicate:predicate];

评论:

  • SELF不需要在谓词中使用。filteredArrayUsingPredicate将谓词应用于GrandParent数组中的每个对象。
  • 在谓词中使用 SUBQUERY 的文档似乎很少。NSExpression 类参考中有一个示例。另请参阅NSPredicate Expression 中 SUBQUERY 的快速解释
  • 在这种情况下,SUBQUERY 中的谓词应用于Parent单个GrandParent. SUBQUERY 返回有任何 10 岁孩子的父母。因此"SUBQUERY(...).@count > 0",如果祖父母至少有一个父母有任何 10 岁的孩子,则评估为 TRUE。

添加:我刚刚发现它实际上可以在没有 SUBQUERY 的情况下完成

NSPredicate *predicate = [NSPredicate
    predicateWithFormat:@"ANY parents.@unionOfArrays.children.age == 10"];

工作并给出所需的结果。(它可能不如 SUBQUERY 有效,但我没有对此进行测试。)

于 2013-05-31T12:16:20.743 回答
12

作为 Martin R 的答案的替代方案,您可以考虑改用块谓词。像这样的东西:

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) {
  GrandParent *grandparent = evaluatedObject;
  for (Parent *parent in grandparent.parents)
    for (Child *child in parent.children)
      if (child.age == 10)
        return YES
  return NO;
}];

假设GrandParent和是各种对象的适当类名ParentChild

就我个人而言,我更喜欢这种形式,因为我总是觉得使用字符串谓词我在代码中混合了语言,我认为这会降低它的可读性。选择显然取决于你。

更新:重新阅读问题后,我现在意识到情况比我最初想象的要复杂。我已经更新了我的答案以循环访问父母和孩子,但 Martin R 的答案现在显然要简单得多。这仍然是一个可以考虑的解决方案。

于 2013-05-31T13:39:45.103 回答