angeldev
angeldev

Reputation: 2053

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.

Upvotes: 11

Views: 14833

Answers (5)

Danny Law
Danny Law

Reputation: 147

Swift 5

let predicate = NSPredicate(format: "id = %@ AND title = %@", argumentArray: ["id value", "title value"])

Upvotes: -2

Abdul Hameed
Abdul Hameed

Reputation: 1043

Swift 3

I found easiest way.

var predicate = NSPredicate(format: "key1 = %@ AND key2 = %@", value1, value2)

Upvotes: 6

jcaron
jcaron

Reputation: 17710

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];

Upvotes: 4

Martin R
Martin R

Reputation: 539705

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)

    // ...
}

Upvotes: 16

vadian
vadian

Reputation: 285069

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"])

Upvotes: 20

Related Questions