Quick Explanation of SUBQUERY in NSPredicate Expression

And for people who don't quite get what the documentation is saying, a SUBQUERY is essentially this:

SUBQUERY(collection, variableName, predicateFormat)

And could (simplistically) be implemented like this:

id resultingCollection = ...; //a new collection, either a mutable set or array
NSMutableDictionary * substitutions = [NSMutableDictionary dictionary];
NSPredicate * p = [NSPredicate predicateWithFormat:predicateFormat];
for (id variable in collection) {
  [substitutions setObject:variable forKey:variableName];
  NSPredicate * filter = [p predicateWithSubstitutionVariables:substitutions];
  if ([filter evaluateWithObject:collection] == YES) {
    [resultingCollection addObject:variable];
  }
}
return resultingCollection;

So in a nutshell, a SUBQUERY is basically taking a collection of objects and filtering out various objects based on the predicate expression of the SUBQUERY, and returning the resulting collection. (And the predicate itself can contain other SUBQUERYs)

Example:

NSArray * arrayOfArrays = [NSArray arrayWithObjects:
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           nil];
NSPredicate * filter = [NSPredicate predicateWithFormat:@"SUBQUERY(SELF, $a, $a.@count > 42)"];
NSArray * filtered = [arrayOfArrays filteredArrayUsingPredicate:filter];
//"filtered" is an array of arrays
//the only arrays in "filtered" will have at least 42 elements each

This is what a subquery evaluates to. (Found from this mailing list thread, the #1 hit for “NSPredicate subquery” in Google.) That bit of documentation also explains how the predicate format string syntax relates to it.


Subquery represents a predicate (third argument - $x IN %@) that is evaluated on all objects (second argument - $x - it's like a variable name in foreach) of a relationship (first argument - Bs). Similarly to regular query returns a list of objects.

I see in many places that people use $x almost dogmatically, but $object in objects relationship makes perfect sense as well (or $city in cities...) :)

I've written a blog post about SUBQUERY some time ago. You can check it here.