NSPredicate with multiple arguments and "AND behaviour" - ios

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.

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

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)
// ...
}

Swift 3
I found easiest way.
var predicate = NSPredicate(format: "key1 = %# AND key2 = %#", value1, value2)

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

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

Related

How to filter an array of dictionary(Datewise) using NSPredicate in swift 4

I am new to the swift, please someone help me. I have to do filter my data datewise. This is my JSON data.
result = (
{
Request = "sample task";
"Requested_by" = 112;
Source = "default-image.jpg";
"created_at" = "2018-07-02 12:44:20";
id = 556;
taskId = 821;
},
{
Request = cvv;
"Requested_by" = 112;
Source = "default-image.jpg";
"created_at" = "2018-07-03 17:01:14";
id = 554;
taskId = 812;
}
)
And I tried some code:
func filterDatesFromMainData(){
let predicate = NSPredicate(format: "created_at = %#", self.datetextField.text!)
self.filterArray = NSMutableArray(array: self.dataArray.filtered(using: predicate))
print("data: \(self.filterArray)")
self.tableView.reloadData()
}
In predicate variable, I will get the date from date picker. In self.filterArray i am getting 0 elements.
Please someone help me, how to solve this task
You can try
let predicate = NSPredicate(format: "created_at contains[c] %#", self.datetextField.text! )
May be your format is not matching with date created_at.
And
Try by replacing
let predicate = NSPredicate(format: "created_at = %#", self.datetextField.text!)
To
let predicate = NSPredicate(format: "created_at contains[c] %#", self.datetextField.text! )

Predicate with many conditions and one argument

I'm trying to query contacts realm db but I've one query string, do I need to repeat it over in the predicate or there's a better way?
let contactResults = realm.objects(ContactRealm.self).filter("is_Contact == true AND full_name CONTAINS[c] %# OR phone_number_one CONTAINS[c] %# OR phone_number_two CONTAINS[c] %# OR phone_number_three CONTAINS[c] %# OR email_address_one CONTAINS[c] %# OR email_address_two CONTAINS[c] %#", query, query, query, query, query, query).sorted(byKeyPath: "full_name", ascending: true)
also is querying like this considered a bad practice?
update: as suggested by a comment here:
let predicateContact = NSPredicate(format: "is_Contact == true")
let fullname = NSPredicate(format: "full_name CONTAINS[c] %#", query)
let phoneNumber = NSPredicate(format: "phone_number_one CONTAINS[c] %#", query)
let phonenumbertwo = NSPredicate(format: "phone_number_one CONTAINS[c] %#", query)
let email = NSPredicate(format: "phone_number_one CONTAINS[c] %#", query)
let secondEmail = NSPredicate(format: "phone_number_one CONTAINS[c] %#", query)
let compoundOr = NSCompoundPredicate(orPredicateWithSubpredicates: [fullname, phoneNumber, phonenumbertwo, email, secondEmail])
let compound = NSCompoundPredicate(type: .and, subpredicates: [predicateContact, compoundOr])
let contactResults = realm.objects(ContactRealm.self).filter(compound).sorted(byKeyPath: "full_name", ascending: true)
Since your predicates look the same and only show a difference in the name of the attribute, I would suggest to create an array of attribute names. Then, you can create an NSPredicate for each name by looping through the names.
let attributes = ["full_name", "phone_number_one", "phone_number_two", "phone_number_three", "email_address_one", "email_address_two"]
let contactPredicateArray = parameters.map {
NSPredicate("\($0) CONTAINS[c] %#", query)
}
let contactPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: contactPredicateArray)
let isContactPredicte = NSPredicate(format: "is_Contact == %#", true)
let predicate = NSCompoundPredicate(type: .and, subpredicates: [isContactPredicte, contactPredicate])
This way, the code is more flexible because removing or adding single attributes to your predicate can be done very easily by modifying the attributes array. It is also more readable because all attributes are defined in one line and not all over the place.

Core data Fetch request error for predicate

Need
I have to find the record with rec_name is "Apple 256GB Macbook" & isActive == true.
Fetch Request
fetchRequest.predicate = NSPredicate(format: "rec_name LIKE[cd] *%#* AND rec_name LIKE[cd] *%#* AND isActive = %#","apple","macbook",true)
Error
Unable to parse the format string "rec_name LIKE[cd] *%#* AND rec_name LIKE[cd] *%#* AND isActive = %#"
Thank you,
The problem is because of *%#* here %# between the * is not replaced by a string argument so NSPredicate is not able to make your query LIKE *apple* and LIKE *macbook* what you need to do is make a search string with wildcards and with Like simply use %#, also as #Vadian suggested use number instead of Bool.
let searchkeyword1 = "apple"
let searchkeyword2 = "macbook"
//Make search string wildcards charaters
let searchString1 = "*\(searchkeyword1)*"
let searchString2 = "*\(searchkeyword2)*"
fetchRequest.predicate = NSPredicate(format: "rec_name LIKE[cd] %# AND rec_name LIKE[cd] %# AND isActive = %#", searchString1,searchString2, true as NSNumber)
Also batter option here is you can use CONTAINS and no need to use of wildcards.
fetchRequest.predicate = NSPredicate(format: "rec_name CONTAINS[cd] %# AND rec_name CONTAINS[cd] %# AND isActive = %#", "apple","macbook", true as NSNumber)
You can do one more thing if you want to find all record start with apple and end with macbook then you can simply use BEGINSWITH and ENDSWITH
fetchRequest.predicate = NSPredicate(format: "rec_name BEGINSWITH[cd] %# AND rec_name ENDSWITH[cd] %# AND isActive = %#", "apple","macbook", true as NSNumber)
Try this
fetchRequest.predicate = NSPredicate(format: "rec_name LIKE[cd] %# AND rec_name LIKE[cd] %# AND isActive = %#","apple","macbook",NSNumber(booleanLiteral: true))
You need to set a predicate query like following:
fetchRequest.predicate = NSPredicate(format: "rec_name == %# AND isActive == %#" , "Apple 256GB Macbook", NSNumber(booleanLiteral: true))
I don't think we need to use Like if you are looking for perfect match.
Try this
NSPredicate(format: "rec_name == %# AND rec_name == %# AND isActive = %d","apple","macbook",true)

NSPredicate use array as filter

I'm trying to use an array as a filter to search Core Data. The values that the fetchRequest returns should have an ID that is in the predicate's array.
let arrayOfFavoriteStampIDS = [302,2300,2309]
fetchRequest.predicate = NSPredicate(format: "itemID IN %i", argumentArray: arrayOfFavoriteStampIDS)
Almost
fetchRequest.predicate = NSPredicate(format: "itemID IN %#", arrayOfFavoriteStampIDS)
argumentArray means something else for example
fetchRequest.predicate = NSPredicate(format: "itemID == %i OR itemID == %i OR itemID == %i", argumentArray:arrayOfFavoriteStampIDS)
In this case each item in the array must match one %i placeholder.

NSPredicate substring in string

I want to show all items where value1 contains value2. I tried this:
let fetchRequest = NSFetchRequest(entityName: "Product")
fetchRequest.predicate = NSPredicate(format: "value1 CONTAINS[cd] value2")
value1, value2 - current object values, it is not variables
But i got error:
Unable to parse the format string
Why it doesn't allow me to do this ?
Try to use this predicate:
let predicate = NSPredicate(format: "value1 CONTAINS[cd] %#", value2)
As were investigated during communication with developer. Issue is in data that is saved to the database. In his case data is saved with quotes ("") and NSPredicate(format: "value1 CONTAINS[cd] %#", value2) is working with errors due to that issue.

Resources