NSPredicate

Format String Summary

Attribute name

Object's attributeName value is equal to value passed in

@"attributeName == %@"

Keypath

Pass a string variable to %K, it will be represented as a keypath, then check if its value is equal to value passed in

"%K == %@"

Templated for predicates

Templated for predicate, checks if the value of key name is in $NAME_LIST. Uses predicateWithSubstitutionVariables

@"%name IN $NAME_LIST"

Substitution predicate

Checks if the constant value name is in $NAME_LIST. Uses predicateWithSubstitutionVariables

@"'name' IN $NAME_LIST"

Example

[NSPredicate predicateWithFormat: @"title == %@", @"minecraft"]

Basic Comparisons

Equal

Left hand expression is equal to right hand expression

=,==

Grater or equal

Left hand expression is greater than or equal to right hand expression

>=,=>

Lesser or equal

Left hand expression is less than or equal to right hand expression

<=,=<

Greater

Left hand expression is greater than right hand expression

>

Lesser

Left hand expression is less than right hand expression

<

Different

Left hand expression is not equal to right hand expression

!=,<>

In

Left hand expression must appear in collection specified by right hand expression, i.e. name IN {'Milk', 'Eggs', 'Bread'}

IN

Between

Left hand expression is between or equal to right hand expression, i.e. 1 BETWEEN {0, 33}. If your left hand expression was 0 or 33 it would also make this true

BETWEEN

Example

[NSPredicate predicateWithFormat: @"expenses BETWEEN {200, 400}"]

Keypath Collection Queries

Average

Returns the average of the objects in the collection as an NSNumber

@avg

Count

Returns the number of objects in a collection as an NSNumber

@count

Min

Returns the minimum value of the objects in the collection as an NSNumber

@min

Max

Returns the maximum value of the objects in the collection as an NSNumber

@max

Sum

Returns the sum of the objects in the collection based on the property

@sum

Example

[NSPredicate predicateWithFormat: @"expenses.@avg.doubleValue < 200"]

Basic Compound Predicates

AND

Logical AND

AND,&&

OR

Logical OR

OR,||

NOT

Logical NOT

NOT,!

Example

[NSPredicate predicateWithFormat: @"age == 40 AND price > 67"]

Object, Array, and Set Operators

Distinct union of objects

Returns an array containing the distinct objects in the property specified by the key path to the right of the operator

@distinctUnionOfObjects

Union of objects

Returns the same as @distinctUnionOfObects except it also includes duplicates

@unionOfObjects

Example

NSArray *payees = [transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"]

Distinct union of arrays

Returns an array containing the distinct objects in the property specified by the key path to the right of the operator

@distinctUnionOfArrays

Union of arrays

Returns the same as @distinctUnionOfArrays except it also includes duplicates

NOT,!

Example

These must be run on an array of arrays. For example if you had:

NSArray *arrayOfTransactions = [[Array of transactions], [Array of transactions]]
NSArray *payees = [arrayOfTransactions valueForKeyPath:@"@distinctUnionOfObjects.payee"]

Distinct union of sets

Returns an NSSet instance containing distinct objects in the property specified by the key path to the right of the operator. Expects an NSSet instance containing NSSet instances

@distinctUnionOfSets

String Comparison Operators

Begins with

Left hand expression begins with the right hand expression

BEGINSWITH

Contains

Left hand expression contains the right hand expression

CONTAINS

Ends with

Left hand expression ends with the right hand expression

ENDSWITH

Like

Left hand expression equals the right hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters

LIKE

Matches

Left hand expression equals the right hand expression using a regex - style comparison

MATCHES

Example

[NSPredicate predicateWithFormat: @"name BEGINSWITH 'm'"]

Aggregate Operators

Some, any

Returns objects where ANY or SOME of the predicate results are true.

ANY,SOME

All

Returns objects where ALL of the predicate results are true.

ALL

None

Returns objects where NONE of the predicate results are true.

NONE

Example

[NSPredicate predicateWithFormat: @"ALL expenses > 1000"]

Array Operations

Array index

Specifies the element at the specified index in the array

array[index]

Array first

Specifies the first element in the array

array[FIRST]

Array last

Specifies the last element in the array

array[LAST]

Array size

Specifies the size of the array

array[Size]

Example

Let's say we have a person with many dogs. index should be replaced with a number which will return the dog that you want to check against.

// Here we're checking if the first dog's age is 5.
[NSPredicate predicateWithFormat: @"dogs[0].age = 5"]
// Here we're checking if a person has 3 dogs
[NSPredicate predicateWithFormat: @"dogs[SIZE] = 3"]

Subqueries

Subquery

Iterates through the collection to return qualifying queries

  • collection - array or set of objects
  • variableName - variable that represents an iterated object
  • predicateFormat - predicate that runs using the variableName
SUBQUERY(collection, variableName, predicateFormat)

Example

Assume this was run on an array of projects. It will return projects with tasks that were not completed by user Alex

[NSPredicate predicateWithFormat: @"SUBQUERY(tasks, $task, $task.completionDate != nil AND $task.user = 'Alex') .@count > 0"]

Tip, Tricks and Examples

Common mistakes

  • Using [NSString stringWithFormat:] to build predicates is prone to have non-escaped diacritics or artifacts like an apostrophe. Use [NSPredicate predicateWithFormat:] instead.
  • Using multiple OR instead of IN, results in repeatable code and can be less efficient
  • When using REGEX and MATCHES, make sure they are the last part of your predicate statement so it does less work. This way objects will be filtered before doing more heavy look ups

Using self

When using a predicate on an array, SELF refers to each object in the array. Here's an example: Imagine you are a landlord figuring out which apartments have to pay their water bill. If you have a list of all the city wide apartments that still need to pay called addressesThatOweWaterBill, we can check that against our owned apartments, myApartmentAddresses.

NSPredicate *billingPredicate = [NSPredicate predicateWithFormat: @"SELF IN %@", addressesThatOweWaterBill]
NSArray *myApartmentsThatOweWaterBill = [myApartmentAddresses filteredArrayUsingPredicate:billingPredicate]

LIKE wildcard match with * and ?

* matches 0 or more characters. For example: Let's say we have an array of names we want to filter

@[@"Sarah", @"Silva", @"silva", @"Silvy", @"Silvia", @"Si*"]

predicateWithFormat: @"SELF == %@", @"Sarah"
// Will return “Sarah”

predicateWithFormat: @"SELF LIKE[c] %@", "Si*"
// Will return “Silva”, “silva”, “Silvy”, “Silvia”, “Si*”

? matches 1 character only

predicateWithFormat: @"SELF LIKE[c] %@", "Silv?"
// Will return “Silva”, “silva”, “Silvy”

Quick tips

CFStringTransform normalizes strings if diacritic insensitive isn't enough. For example you could turn Japanese characters into a Latin alphabetic representation. It's extremely powerful with a lot of methods that you can see here: http://nshipster.com/cfstringtransform/

Make sure your columns are indexed to improve performance of using IN operators

  • [c] case insensitive: lowercase & uppercase values are treated the same

  • [d] diacritic insensitive: special characters treated as the base character

predicateWithFormat: @"name CONTAINS[c] 'f'"

Keypath collection queries

Keypath collection queries work best when you work with a lot of numbers. Being able to call the min or max, adding things up, and then filtering results are simpler when you only have to append an extra parameter. By having an array of expenses, you can do a quick check on if something is below or above a range of allowed expenses.

[NSPredicate predicateWithFormat: @"expenses.@avg.doubleValue < 200"]

How subqueries work

SUBQUERY(collection, variableName, predicate)

A subquery takes a collection then iterates through each object (as variableName) checking the predicate against that object. It works well if you have a collection (A) objects, and each object has a collection (B) other objects. If you're trying to filter A based on 2 or more varying attributes of B.

predicateWithFormat: @"SUBQUERY(tasks, $task, $task.completionDate != nil AND $task.user = 'Alex') .@count > 0"

SUBQUERY(…) returns an array. We need to check if its count > 0 to return the true or false value predicate expects.

Notes