NSPredicate subquery syntax

老子叫甜甜 提交于 2019-11-30 05:27:12

Man, "unfriendly" is an understatement on that array!

OK, I think I figured this out:

NSArray *dataRows = @[
                      @{ @"row" : @"1",
                         @"row_values" : @[
                                           @{ @"property_id" : @"47cc67093475061e01000542", 
                                              @"property_value" : @"Mr." },
                                           @{ @"property_id" : @"47cc67093475061e01000540", 
                                              @"property_value" : @"Male" }
                                          ]
                       },
                      @{ @"row" : @"2",
                         @"row_values" : @[
                                           @{ @"property_id" : @"47cc67093475061e01000542", 
                                              @"property_value" : @"Ms." },
                                           @{ @"property_id" : @"47cc67093475061e01000540", 
                                              @"property_value" : @"Female" }
                                          ]
                       }
                     ];

NSPredicate *p = [NSPredicate predicateWithFormat:@"SUBQUERY(row_values, $rv, $rv.property_id = %@ AND $rv.property_value = %@).@count > 0", @"47cc67093475061e01000540", @"Male"];

NSArray *filtered = [dataRows filteredArrayUsingPredicate:p];

So let's see what this predicate is doing.

  1. Start with the outer-most level:

    SUBQUERY([stuff]).@count > 0
    

    A SUBQUERY returns an array of objects. We're going to run this SUBQUERY on every NSDictionary in the dataRows array, and we want to aggregate all of the dictionaries where the SUBQUERY on that dictionary returns something. So we run the SUBQUERY, and then (since it returns a collection), ask it for how many items were in it (.@count) and see if that's greater than 0. If it is, then the top-level dictionary will be in the final filtered array.

  2. Dig in to the SUBQUERY:

    SUBQUERY(row_values, $rv, $rv.property_id = %@ AND $rv.property_value = %@)
    

    There are three parameters to every SUBQUERY: A key path, a variable, and a predicate. The key path is the property of the object that we're going to be iterating. Since the SUBQUERY is being evaluated on the outer-most dictionaries, we're going to ask for the @"row_values" of that dictionary and get back an array. The SUBQUERY will then iterate over the items in the row_values collection.

    The variable is what we're going to call each item in the collection. In this case, it's simply going to be $rv (shorthand for "row value"). In our case, each $rv will be an NSDictionary, since the row_values "property" is an array of dictionaries.

    Finally, the predicate is going to be executed, with the $rv getting replaced for each dictionary in turn. In this case, we want to see if the dictionary has a certain property_id and a certain property_value. If it does, it will be aggregated into a new array, and that is the array that will be returned from the SUBQUERY.

    So to say it a different way, the SUBQUERY is going to build an array of all the row_values that have a property_id and property_value of what we're looking for.

And finally, when I run this code, I get:

(
        {
        row = 1;
        "row_values" =         (
                        {
                "property_id" = 47cc67093475061e01000542;
                "property_value" = "Mr.";
            },
                        {
                "property_id" = 47cc67093475061e01000540;
                "property_value" = Male;
            }
        );
    }
)
Senseful

Apple's SUBQUERY documentation is scattered throughout several places.

The string format for a subquery expression is:

SUBQUERY(collection_expression, variable_expression, predicate);

where expression is a predicate expression that evaluates to a collection, variableExpression is an expression which will be used to contain each individual element of collection, and predicate is the predicate used to determine whether the element belongs in the result collection.

For more details and examples, see my answer to a similar question about the documentation for SUBQUERY syntax.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!