'NSPredicate with multiple arguments and "AND behaviour"
I have a function which fetches those objects from a particular entity that fulfill a specified criterion:
func fetchWithPredicate(entityName: String, argumentArray: [AnyObject]) -> [NSManagedObject] {
let fetchRequest = NSFetchRequest(entityName: entityName)
fetchRequest.predicate = NSPredicate(format: "%K == %@", argumentArray: argumentArray)
do {
return try self.managedContext.executeFetchRequest(fetchRequest) as! [NSManagedObject]
} catch {
let fetchError = error as NSError
print(fetchError)
return [NSManagedObject]()
}
}
When I pass an array with multiple aguments (multiple attribute names and values) it seems that creates a query like this:
attributeName1 = value1 OR attributeName2 = value2 OR attributeName3 = value3 OR...
I would like to change these ORs for ANDs.
EDIT:
The problem was that it was only replacing the first two items with "%K == %@".
So, I have to create a NSCompoundPredicate to create a predicate dynamically.
Solution 1:[1]
To create a predicate (with a variable number of expressions) dynamically, use NSCompoundPredicate
, e.g.
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: subPredicates)
where subPredicates
is a [NSPredicate]
array, and each subpredicate
is created from the provided arguments in a loop with
NSPredicate(format: "%K == %@", key, value)
Something like this (untested):
func fetchWithPredicate(entityName: String, argumentArray: [NSObject]) -> [NSManagedObject] {
var subPredicates : [NSPredicate] = []
for i in 0.stride(to: argumentArray.count, by: 2) {
let key = argumentArray[i]
let value = argumentArray[i+1]
let subPredicate = NSPredicate(format: "%K == %@", key, value)
subPredicates.append(subPredicate)
}
let fetchRequest = NSFetchRequest(entityName: entityName)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: subPredicates)
// ...
}
Solution 2:[2]
In your example the %K
and %@
format tokens are replaced only by the first two items of the argument array, the other items are ignored.
You have to provide all format tokens to be replaced in the string for example
NSPredicate(format: "%K == %@ AND %K == %@", argumentArray:["key1", "value1", "key2", "value2"])
Solution 3:[3]
Swift 3
I found easiest way.
var predicate = NSPredicate(format: "key1 = %@ AND key2 = %@", value1, value2)
Solution 4:[4]
You could use a compound predicate. I'll give you the Objective-C syntax, I'll leave the translation to Swift as an exercise...
If your keys and values are in a dictionary:
NSDictionary *conds = @{ @"key1": @"value1", @"key2", @"value2" };
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:conds.allKeys.length];
for (NSString *key in conds.allKeys)
{
[predicates addObject:[NSPredicate predicateWithFormat:@"%K == %@", key, conds[key]]];
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
Solution 5:[5]
Swift 5
let predicate = NSPredicate(format: "id = %@ AND title = %@", argumentArray: ["id value", "title value"])
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | vadian |
Solution 3 | Abdul Hameed |
Solution 4 | jcaron |
Solution 5 | Danny Law |