Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSPredicate nested relationship

I am trying to filter a mutable array of objects using NSPredicate and am having trouble access the level that contains the property I would like to filter on.

To give a simplified example consisting of the similar custom objects.

  • Grandparent
  • Parent
  • Child

I have an NSMutableArray of Grandparents and I would like to find all the Grandparent Objects that have GrandChildren of age 10. Therefore the grandchildren are two levels deep from the root. Child has an age property amongst other things.

ie. Grandparents has an array property Parents and Parents has an array property Children and Children has an integer property age.

The following NSPredicate has returned no results. "SELF.parents.children.age == 10".

I realise that as these are nested collections this predicate is likely the wrong way to go about it but I am stuck as to how to access that level. Perhaps via a Subquery or Collection Operator but I cannot work it out.

One thing to keep in mind is that I obviously still want GrandParents that have multiple Grandchildren of different ages, one of which is aged 10.

like image 788
Daniel Bowden Avatar asked May 31 '13 12:05

Daniel Bowden


1 Answers

The "obvious" solution would be the predicate:

"ANY parents.children.age == 10"

However, the "ANY" operator does not work with nested to-many relationships. Therefore, you need a 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];

Remarks:

  • Using SELF in the predicate is not necessary. filteredArrayUsingPredicate applies the predicate to each GrandParent object in the array.
  • The usage of SUBQUERY in predicates seems to be poorly documented. There is one example in the NSExpression class reference. See also Quick Explanation of SUBQUERY in NSPredicate Expression.
  • In this case, the predicate inside SUBQUERY is applied to each Parent of a single GrandParent. The SUBQUERY returns the parents that have any child aged 10. So "SUBQUERY(...).@count > 0" evaluates to TRUE if the grandparent has at least one parent that has any child aged 10.

ADDED: I just found out that it can actually be done without SUBQUERY:

NSPredicate *predicate = [NSPredicate
    predicateWithFormat:@"ANY [email protected] == 10"];

works and gives the desired result. (It could be less effective than the SUBQUERY, but I did not test that.)

like image 171
Martin R Avatar answered Sep 19 '22 03:09

Martin R