In my code below I am using NSPredicates specifically I am using NSCompoundPredicate to check for multiple different parameters in my search function. How would I go about searching with both the First Name AND Last Name, I am currently using AND but it does not return anything in my UITableView. All my other predicates listed in the compound predicate work great.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
textInSearchField = searchBar.text!
if searchBar.text == ""{
print("Searching for all clients.")
retrieveClients()
view.endEditing(true)
clientTableView.reloadData()
}else{
print("Enter a client name to search.")
isFilteringSearch = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
let firstNamePredicate = NSPredicate(format: "firstName = %#", textInSearchField)
let lastNamePredicate = NSPredicate(format: "lastName = %#", textInSearchField)
let idPredicate = NSPredicate(format: "id = %#", textInSearchField)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstAndLast,firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
clients = try! context.fetch(clientsEntity) as! [NSManagedObject]
view.endEditing(true)
clientTableView.reloadData()
}
}
Something to bear in mind is that I still need to be able to use
LogicalType.or since I want to have the option for the user to search by first name, last name but also a combination of both for example Harold Finch or Finch/Harold etc.
Cheers!
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
let firstNamePredicate = NSPredicate(format: "firstName = %#", textInSearchField)
let lastNamePredicate = NSPredicate(format: "lastName = %#", textInSearchField)
let idPredicate = NSPredicate(format: "id = %#", textInSearchField)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstAndLast,firstNamePredicate, lastNamePredicate, idPredicate])
I hope that for orPredicate, it should be [firstAndLast, idPredicate] instead, but you didn't make it work. Else you need to rethink about it, that's your orPredicate current logic:
if ((a == x AND b == x) OR (a == x) OR (b == x) OR (c = x)) {
}
The (a == x AND b == x) is useless.
There is an issue on that line:
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField)
You have two placeholders (%#), but only one argument (textInSearchField).
let firstAndLast = NSPredicate(format: "firstName = %# AND lastName = %#", textInSearchField, textInSearchField)
Thank for the help, I ended up using an alternative method with contains[c] my code is below for anyone who needs help with a similar function, you can use contains[c](contains character) to search via character specifically this applies to all strings i.e first name or last name and in my case also an ID which is stored as a String also.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
textInSearchField = searchBar.text!
if searchBar.text == ""{
retrieveClients()
view.endEditing(true)
clientTableView.reloadData()
}else{
isFilteringSearch = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let words = textInSearchField.components(separatedBy: " ")
for word in words{
if (word).count == 0{
continue
}
let firstNamePredicate = NSPredicate(format: "firstName contains[c] %#", word)
let lastNamePredicate = NSPredicate(format: "lastName contains[c] %#", word)
let idPredicate = NSPredicate(format: "id contains[c] %#", word)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
}
clients = try! context.fetch(clientsEntity) as! [NSManagedObject]
view.endEditing(true)
clientTableView.reloadData()
}
}
I followed the second answer on this post NSPredicate: Combine CONTAINS with IN
Thank You
Related
My Code
isFiltering = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let words = textInSearchField.components(separatedBy: " ")
for word in words{
if (word).count == 0{
continue
}
let firstNamePredicate = NSPredicate(format: "firstName contains[c] %#", word)
let lastNamePredicate = NSPredicate(format: "lastName contains[c] %#", word)
let idPredicate = NSPredicate(format: "id contains[c] %#", word)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
clientResults = try! context.fetch(clientsEntity) as! [NSManagedObject]
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
My data model:
I am using a tableView to display my data which works great, now I have implemented a filter function which allows the user to search based on a Clients first name, last name, id etc using NSCompoundPredicate.
I then sort the resulting [NSManagedObject] by date using NSSortDescriptor, my aim is to set my clientResults variable to contain the SORTED contents of the NSSet. My print statement only outputs that there is one Assessment inside the results variable when in actual fact the NSSet contains two of these NSManagedObjects.
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
// MARK - I enumerate the contents of the sorted array.
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
What is the best practice for assigning the contents of the NSSet to a variable of type [NSManagedObject]?
Thank you.
If you know that elements in NSSet are of type NSManagedObject why not just do
let managedObjectsArray = set.allObjects
or if you want to make sure it is of correct type you can do:
if let managedObjectsArray = set.allObjects as? [NSManagedObject] {
//do what you want with [NSManagedObject] array
}
How can i combine a mix AND OR condition in Swift predicate. I have a following query
Select * from tblTemp where dept == 1 && (subdept == 11 || subdept == 12)
I can write two predicate with same operator but don't know how to combine them
let deptPredicate = NSPredicate(format: "dept == %#", 1)
let subdeptPredicate1 = NSPredicate(format: "subdept = %#", 11)
let subdeptPredicate2 = NSPredicate(format: "subdept = %#", 12)
let andPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [deptPredicate, subdeptPredicate1])
NSCompoundPredicate is a subclass of NSPredicate, which means
that the result of
NSCompoundPredicate(type:subpredicates:) can be used in another compound
predicate.
Note however that the %# format placeholder expects an NSObject
instance:
let deptPredicate = NSPredicate(format: "dept == %#", 1 as NSNumber)
let subdeptPredicate1 = NSPredicate(format: "subdept = %#", 11 as NSNumber)
let subdeptPredicate2 = NSPredicate(format: "subdept = %#", 12 as NSNumber)
let orPredicate = NSCompoundPredicate(type: .or,
subpredicates: [subdeptPredicate1, subdeptPredicate2])
let andPredicate = NSCompoundPredicate(type: .and,
subpredicates: [deptPredicate, orPredicate])
Alternatively, use the %ld format for integers:
let deptPredicate = NSPredicate(format: "dept == %ld", 1)
// ... etc.
There are also convenience initializers:
let orPredicate = NSCompoundPredicate(orPredicateWithSubpredicates:
[subdeptPredicate1, subdeptPredicate2])
let andPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:
[deptPredicate, orPredicate])
Compound predicates are very useful to combine a dynamic set of
conditions at runtime. On the other hand, if only the values change
then you can simply use "AND" and "OR" within the predicate
format string:
NSPredicate(format: "dept == %ld AND (subdept = %ld OR subdept = %ld)", 1, 11, 12)
Finally note that you can use the #keyPath directive with the
%K placeholder, so that the compiler fills in the correct property
name (thus reducing the chance of typographical errors):
let deptPredicate = NSPredicate(format: "%K == %ld", #keyPath(MyEntity.dept), 1)
I implements OR filter,
if user types "NBA LA",
Any item in this array "appDel.dataFetcher?.appTitles" will be searched with OR mode.
However, I don't know how to implement AND mode.
That is any selected item must have NBA and LA in its text at the same time
OR SEARCH
let searchTerms = searchController.searchBar.text!.characters.split{$0 == " "}.map(String.init)
for term: String in searchTerms {
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", term)
let array = ((appDel.dataFetcher?.appTitles)! as NSArray).filteredArrayUsingPredicate(searchPredicate)
searchResult += array as! [String]
}
let mySet = Set<String>(searchResult)
You can build up your resulting AND predicate using NSCompoundPredicate(andPredicateWithSubpredicates:) like below:
let predicates = searchTerms.map {
return NSPredicate(format: "SELF CONTAINS[c] %#", $0)
}
let searchPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
let searchResult = ((appDel.dataFetcher?.appTitles)! as NSArray).filteredArrayUsingPredicate(searchPredicate)
let mySet = Set<String>(searchResult)
FYI, you can split your text much more easier using componentsSeparatedByString:
let searchTerms = searchController.searchBar.text!.componentsSeparatedByString(" ")
What am I doing wrong ?
import UIKit
var values:NSMutableArray = []
let url = NSURL(string: "URL") // PHP JSON Result
let data = NSData(contentsOfURL: url!)
values = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSMutableArray
var searchtext = "ROYAL"
let resultPredicate = NSPredicate(format: "SELF contains[cd] %#", searchtext)
let filteredCars = values.filteredArrayUsingPredicate(resultPredicate)
Change your code like this
let resultPredicate = NSPredicate(format: "SELF.Vendor contains[cd] %# OR SELF.vendor contains[cd] %# OR SELF.email contains[cd] %#", searchtext, searchtext, searchtext)
let filteredCars = values.filteredArrayUsingPredicate(resultPredicate)
You need to add Key in which you want to filter the text.
Hope this will help you.
EntityA: Gymnast
firstName
lastName
one to many MeetResults
EntityB: MeetResults
meetDate
barScore
beamScore
floorScore
vaultScore
I am loading all gymnasts who are set as active into the picker. As I create an NSSET result.meetresults = meetScore.copy() as? NSSet which works fine I am trying to figure out how to remove a gymnast who already has a NSSET for this date. Any assistance would be appreciated.
func getGymnasts() {
let fetchRequest = NSFetchRequest(entityName: "Gymnast")
let sortDescriptor1 = NSSortDescriptor(key: "fullName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1]
let filter1 = NSPredicate(format: "isActive == %#", "Yes")
let filter2 = NSPredicate(format: "ANY meetresults.meetDate != %#", "\(meetDateText.text!)")
let predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [filter1, filter2])
fetchRequest.predicate = predicate
//fetchRequest.predicate = filter1
do {
self.gymnastArray = try AD.managedObjectContext.executeFetchRequest(fetchRequest) as! [Gymnast]
self.gymnastPicker.reloadAllComponents()
} catch {
fatalError("Fetch Failed")
}
}
To-many relationships are awkward sometimes. You can use the subquery syntax to query the count of an internal fetch to find Meets matching your criteria, then check there aren't any:
let filter2 = NSPredicate(format: "SUBQUERY(meetresults.meetDate, $m, $m.meetDate == %#).#count == 0", meetDateText.text!)