Search code examples
iosobjective-csumoperatorsnspredicate

Objective C NSPredicate sum of two properties


I'm Trying to create a NSPredicate what can do the sum of two properties and compare it to another property of an object:

Object.property1 + Object.property2 < Object.property3

I tried (%K + %K) < %K but with no success.

PS: Should work even some of properties are null.

I'm trying to avoid making a lot of compound predicates.

Thanks, Florin


Solution

  • Assuming you are using a SQLite persistent store, the fetch predicate is converted into an equivalent SQLite WHERE clause. The handling of NULL values is therefore determined by SQLite. From the SQLite documentation:

    Adding anything to null gives null

    so if any of the attributes in your predicate is NULL, the sum will be NULL. The Predicate Programming Guide is also explicit that NULL values must be tested separately:

    A comparison predicate does not match any value with null except null (nil) or the NSNull null value .... If you want to match null values, you must include a specific test in addition to other comparisons ...

    You are therefore faced with having to explicitly test for null values in your predicate, as @Kamil.S has done in his answer. There is one way to make it slightly easier to read and handle: replace the property values with a conditional expression (equivalent to property1 == nil ? 0 : property1). You can do this with NSExpression (see documentation):

    NSExpression *prop1 = [NSExpression expressionForConditional:[NSPredicate predicateWithFormat:@"property1 == nil"] 
                                        trueExpression: [NSExpression expressionForConstantValue: @0] 
                                        falseExpression: [NSExpression expressionForKeyPath:@"property1"]];
    

    (and similarly for property2, etc). You can then substitute these NSExpression values into your predicate using the %@ format specifier:

    fetch.predicate = [NSPredicate predicateWithFormat:@"%@ + %@ < %@", prop1, prop2, prop3];
    

    CoreData parses the conditional expression into a SQLite CASE (...) when 1 then (...) else (...) END clause which is evaluated in the database as part of the fetch: but I have no idea how performant that will be compared to the lengthy compound predicate. (I have also only tested using a single conditional expression; I assume CoreData can handle two or more in a single predicate, but leave it for you to confirm.)