【问题标题】:NSPredicate nested relationshipNSPredicate 嵌套关系
【发布时间】:2013-05-27 06:06:06
【问题描述】:

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

举一个由相似的自定义对象组成的简化示例。

  • 祖父母
  • 父母
  • 孩子

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

即。祖父母有一个数组属性父母和父母有一个数组属性孩子和孩子有一个整数属性年龄。

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

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

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

【问题讨论】:

    标签: iphone ios objective-c nsmutablearray nspredicate


    【解决方案1】:

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

    "ANY parents.children.age == 10"
    

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

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

    备注:

    • 不需要在谓词中使用SELFfilteredArrayUsingPredicate 应用 数组中每个 GrandParent 对象的谓词。
    • 在谓词中使用 SUBQUERY 的文档似乎很少。 NSExpression 类引用中有one example。另见Quick Explanation of SUBQUERY in NSPredicate Expression
    • 在这种情况下,SUBQUERY 中的谓词应用于单个GrandParent 的每个Parent。 SUBQUERY 返回有任何 10 岁孩子的父母。 因此,如果祖父母至少有一个父母有任何 10 岁的孩子,"SUBQUERY(...).@count > 0" 的计算结果为 TRUE。

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

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

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

    【讨论】:

    • 有趣的解决方案。我只是想了解语法。鉴于我从祖父母数组开始并且必须深入两个级别,我是否必须从 SELF.parents 开始?否则子查询如何知道父母?如果可能的话,您能否将它包装在一些 ObjC NSPredicate 示例中?
    • @DanielBowden:稍后我会在我的电脑旁展开答案。
    • 这也是缺少一个额外的 ( 在开始时?为什么 count>0 是必要的,这不是在查询中暗示的吗?
    • @DanielBowden:是的,谓词中有语法错误。我已经解决了这个问题并添加了明确的(经过测试的!)代码和一些解释。 - 希望对您有所帮助,否则如果您需要更多信息,请告诉我。
    【解决方案2】:

    作为 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;
    }];
    

    假设GrandParentParentChild 是各种对象的适当类名。

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

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

    【讨论】:

    • 是的,你是对的! - (即使问题是关于数组的,我在给出答案时也会想到 Core Data,并且基于块的谓词不适用于 Core Data 获取请求。)
    • 我同意,这很清楚。但是这种格式不仅仅适用于字典数组。但是 formatString 的工作要归功于 KVC。在这种情况下,我们得到 [evaluatedObject valueForKeyPath:@"parents"] 等。
    • @Vignesh 我没有看到 OP 提到任何字典,但你说得对,如果没有至少一个演员(我现在已经添加到答案)。但是,重新阅读问题后,即使这样还不够,因为 parent 和 children 属性是数组。
    • 感谢您更新答案。我说的字典和这个问题无关。
    • 哦,我明白了 - 你的意思是作为一个通用的解决方案。这绝对是支持字符串谓词的另一点。不过,我仍然偏爱使用积木。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-27
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多