Format string summary
@"attributeName == %@"
@"%K == %@"
@"%name IN $NAME_LIST"
@"'name' IN $NAME_LIST"
[NSPredicate predicateWithFormat: @"title == %@", @"minecraft"]
Keypath collection queries
@avg
@count
@min
@max
@sum
[NSPredicate predicateWithFormat: @"expenses.@avg.doubleValue < 200"]
Object, array, and set operators
@distinctUnionOfObjects
@unionOfObjects
NSArray *payees = [transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"]
@distinctUnionOfArrays
@unionOfArrays
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"]
@distinctUnionOfSets
Array operations
array[index]
array[FIRST]
array[LAST]
array[SIZE]
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"]
Basic comparisons
=,==
>=,=>
<=,=<
>
<
!=,<>
IN
BETWEEN
[NSPredicate predicateWithFormat: @"expenses BETWEEN {200, 400}"]
Basic compound predicates
AND,&&
OR,||
NOT,!
[NSPredicate predicateWithFormat: @"age == 40 AND price > 67"]
String comparison operators
BEGINSWITH
CONTAINS
ENDSWITH
LIKE
MATCHES
[NSPredicate predicateWithFormat: @"name BEGINSWITH 'm'"]
Aggregate operators
ANY,SOME
ALL
NONE
[NSPredicate predicateWithFormat: @"ALL expenses > 1000"]
Subqueries
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
[NSPredicate predicateWithFormat: @"SUBQUERY(tasks, $task, $task.completionDate != nil AND $task.user = 'Alex') .@count > 0"]
Assume this was run on an array of projects. It will return projects with tasks that were not completed by user Alex
Tips, Tricks, & 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 OR
OR
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
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.
Receive news and updates from Realm straight to your inbox