I have created an searchController and therefor i'm trying t make it filter content according to the text in the UISearchController. I've created a custom Object looking like below. I've tried using NSPredicate, but keep getting:
cannot convert value of type NSPredicate to expected type #noescape (organization) throws...
class Organization: Object {
var id: Int = 0
var name: String = ""
var shortName: String = ""
var image: NSData = NSData()
var pinImage: NSData = NSData()
let locations = List<Location>()
}
Then I have an array called sortedLocations in my ViewController which contains a number of Organization Objects.
What I've tried so far:
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF.name CONTAINS[c] %#", searchController.searchBar.text!)
let array = sortedLocations.filter(searchPredicate)
filteredTableData = array as! [Organization]
self.tableView.reloadData()
}
The filter() method of SequenceType does not take an NSPredicate
as an argument, but a closure, e.g.
let filteredTableData = sortedLocations.filter {
$0.name.localizedCaseInsensitiveContainsString(searchText)
}
The closure is called for each array element (here using the shorthand
argument $0) and returns true or false to indicate if the element
is to be included in the filtered result or not.
You can use an NSPredicate to filter an NSArray, that would look
like
let filtered = someNSArray.filteredArrayUsingPredicate(predicate)
but there is no reason to use this if you have a Swift array.
Related
I have a class such as this
class FoundItem : NSObject {
var id : String!
var itemName : String!
var itemId : Int!
var foundBy : String!
var timeFound : String!
init(id: String,
itemName: String,
itemId: Int,
foundBy: String,
timeFound: String)
{
self.id = id
self.itemName = itemName
self.itemId = itemId
self.foundBy = foundBy
self.timeFound = timeFound
}
and I reference it on my
class MapViewVC: UIViewController, MKMapViewDelegate {
var found = [FoundItem]()
var filterItemName : String()
}
My FoundItem are generated by into an array of dictionaries from my class of FoundItem from a firebase query. I then get a string of that itemName that is generated from an another view controller that is a collection view on the didSelection function. I want to take that string and then filter or search the arrays with the string itemName that is equal from the itemName string from my previous viewController. Then removed the array of dictionaries that are not equal to the itemName. Not just the objects, but the entire array that contains non-equal key, value pair. I have looked for days, and I am stuck on filtering an array of dictionaries created from a class. I have looked and tried NSPredicates, for-in loops, but all that ends up happening is creating a new array or bool that finds my values or keys are equal. Here is the current function I have written.
func filterArrayBySearch() {
if self.filterItemName != nil {
dump(found)
let namePredicate = NSPredicate(format: "itemName like %#", "\(filterItemName)")
let nameFilter = found.filter { namePredicate.evaluate(with: $0) }
var crossRefNames = [String: [FoundItem]]()
for nameItemArr in found {
let listName = nameItem.itemName
let key = listName
if crossRefNames.index(forKey: key!) != nil {
crossRefNames[key!]?.append(nameItemArr)
if !("\(key)" == "\(filterItemName!)") {
print("------------- Success have found [[[[[[ \(key!) ]]]]]] and \(filterItemName!) to be equal!!")
// crossRefNames[key!]?.append(nameItemArr)
} else {
print("!! Could not find if \(key!) and \(filterItemName!) are equal !!")
}
} else {
crossRefNames[key!] = [nameItemArr]
}
}
} else {
print("No Data from Search/FilterVC Controller")
}
}
Can anyone help? It seems like it would be the simple task to find the value and then filter out the dictionaries that are not equal to the itemName string, but I keep hitting a wall. And running into for-in loops myself :P trying different things to achieve the same task.
I hope I understood what you were asking. You mention an "array of dictionaries" but you don't actually have an array of dictionaries anywhere in the code you've posted.
As far as I can tell, you are asking how to find all the entries in the found array for which itemName equals the filterItemName property.
If so, all you should need to do is:
let foundItems = found.filter { $0.itemName == filterItemName }
That's it.
Some other ideas:
If you want to search for items where filterItemName is contained in the itemName, you could do something like this:
let foundItems = found.filter { $0.itemName.contains(filterItemName) }
You could also make use of the lowercased() function if you want to do case-insensitive search.
You could also return properties of your found elements into an array:
let foundIds = found.filter { $0.itemName == filterItemName }.map { $0.itemId }
Sort array of dictionary using the following way
var dict:[[String:AnyObject]] = sortedArray.filter{($0["parentId"] as! String) == "compareId"}
The filter function loops over every item in a collection, and returns a collection containing only items that satisfy an include condition.
We can get single object from this array of dictionary , you can use the following code
var dict = sortedArray.filter{($0["parentId"] as! String) == "compareId"}.first
OR
let dict = sortedArray.filter{ ($0["parentId"] as! String) == "compareId" }.first
Local search filter using predicate in array of dictionary objects
with key name this code use for both swift3 and swift4,4.1 also.
func updateSearchResults(for searchController:
UISearchController) {
if (searchController.searchBar.text?.characters.count)! > 0 {
guard let searchText = searchController.searchBar.text,
searchText != "" else {
return
}
usersDataFromResponse.removeAll()
let searchPredicate = NSPredicate(format: "userName
CONTAINS[C] %#", searchText)
usersDataFromResponse = (filteredArray as
NSArray).filtered(using: searchPredicate)
print ("array = \(usersDataFromResponse)")
self.listTableView.reloadData()
}
}
Here I use CoreData And I have Array of Dictionary .
Here I am filter the key paymentToInvoice that value is invoice array then key invoiceToPeople that key contain People Dictionary then I search FirstName, lastName, Organization multiple key contain searchText.
I hope it's helps Please try this Thank You
var searchDict = dict.filter { (arg0) -> Bool in
let (key, value) = arg0
for paymentInfo in (value as! [PaymentInfo]){
let organization = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.organization
let firstName = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.firstName
let lastName = (Array((value as! [PaymentInfo])[0].paymentToInvoice!)[0] as! InvoiceInfo).invoiceToPeople?.lastName
return organization?.localizedStandardRange(of: searchText) != nil || firstName?.localizedStandardRange(of: searchText) != nil || lastName?.localizedStandardRange(of: searchText) != nil
}
return true
}
I have two classes like below:
class City : NSObject{
var header:String? = nil
var areas:NSMutableArray? = nil //Contain array of Area
//Return array of City objects
class func generate(cityCount:NSInteger) -> NSMutableArray{...}
}
and
class Area : NSObject{
var title:String? = nil
var address:String? = nil
}
//Return array of Area objects
class func generate(areaCount:NSInteger) -> NSMutableArray {...}
Now, I have Array of City like this declared in my viewcontroller:
var cities = City.generate(200)
and when I search the header inside using NSPredicate it work perfectly
let pred = NSPredicate(format: "SELF.header CONTAINS %#",searchString)
let filteredCities = self.cities.filteredArrayUsingPredicate(pred)
But when I search the cities->areas->address (I want to search address). It is not working. It is always return 0 object. Here what I am trying:
let pred = NSPredicate(format: "SELF.areas.address CONTAINS %#",searchString) //name
let filteredCities = (self.cities as NSArray).filteredArrayUsingPredicate(pred)
EDIT
I need only the area object that contain matching address.
I have tried:
let pred = NSPredicate(format: "ANY areas.address CONTAINS %#",searchString)
This is giving the City object with all area object.
Thanks in advance.
Have try like this
let pred = NSPredicate(format: "ANY areas.address CONTAINS %#",searchString)
Try this bro,
let pred = NSPredicate(format: "SELF contains[c] %#",searchString)
let filteredCities = self.area.filteredArrayUsingPredicate(pred)
class ListViewController: UITableViewController, UISearchResultsUpdating {
var list = [MovieVO]()
var filteredlist = [MovieVO]()
var resultSearchController = UISearchController()
func updateSearchResultsForSearchController(searchController: UISearchController)
{
self.filteredlist.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "title CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.list as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredlist = array as! [String] => Cannot assign a value of type '[String]' to a value of type '[MovieVO]'
self.tableView.reloadData()
}
You have defined self.filteredlist as a Swift array of MovieVO types, so no, you cannot assign to it an array of String types. Try and cast it to a MovieVO type or otherwise convert it to that type (perhaps it would be useful to see your definition of MovieVO).
You've declared filteredlist as an array of MovieVO when you did:
var filteredlist = [MovieVO]()
So it can't accept [String], only [MovieVO].
I guess instead of casting your NSArray to [String] you wanted to cast it back to [MovieVO]:
self.filteredlist = array as! [MovieVO]
And to avoid crashes, replace this forced cast with optional binding:
if let movies = array as? [MovieVO] {
self.filteredlist = movies
}
I've the following array of objects which I'm trying to filter and return for a searchDosplayController.
var family = [Family]()// fetchedFamily
var filteredFamily: [Family]! // filter fetched events
And that is how, I'm filtering it:
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = searchController.searchBar.text
self.filteredProvince = provinces
if !searchText.isEmpty {
let searchPredicate = NSPredicate(format: "name CONTAINS[c] %#", searchText)
let array = (filteredProvince as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredProvince = array as! [Province]
}
However nothing is getting returned when I'm searching. And I tried to do it in this way:
filteredFamily = searchText.isEmpty ? family : family.filter({(dataString: String) -> Bool in
return dataString.rangeOfString(searchText, options: .CaseInsensitiveSearch) != nil
})
But, I'm receiving the following error: 'Family is not a subtype of String'. Is there any better way to filter the Family? Because, the filtered result has to be sent back to searchDisplayController.
Thanks in advance.
So we have a Family class that does look like this right?
class Family {
let name : String
init(name:String) {
self.name = name
}
}
Then we have a list of families:
var families = [Family]()
And we want to extract all the families where the name property contains a given text.
let searchText = "something here"
Good, first of all we add this extension to the String struct.
extension String {
func contains(find: String) -> Bool {
return self.rangeOfString(find) != nil
}
}
And finally we can filter the families writing:
let filtered = families.filter { $0.name.contains(searchText) }
Hope this helps.
I'm trying to create a search function using the UISearchController. However i cant seem to make it work with my Team Object. I've started by creating a Team Object which contain a id, name and shortname. Then i'm retrieving the teamData from a url and adding the Team Objects into an array which is populated into a tableView. This tableView contain a searchController which is suppose to filter the Data, but nothing happens.
arrays
var teamArray = Array<Team>()
var filteredTableData = [String]()
GetTeams function
func getTeams(url: String) {
isApiCalling = true
request(.GET, url, parameters: nil)
.response { (request, response, data, error) in
if error == nil {
let data: AnyObject = data!
let jsonArray = JSON(data: data as! NSData)
for (key: String, subJson: JSON) in jsonArray {
// Create an object and parse your JSON one by one to append it to your array
var newTeamObject = Team(id: subJson["id"].intValue, name: subJson["name"].stringValue, shortname: subJson["shortname"].stringValue)
self.teamArray.append(newTeamObject)
}
self.isApiCalling = false
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
}
CellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("teamCell", forIndexPath: indexPath) as! TeamCell
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Light", size: 20)
cell.textLabel?.text = self.teamArray[indexPath.row].name as String
if (self.cellSelected.containsObject(indexPath)) {
cell.accessoryView = cell.accessoryCheck
} else {
cell.accessoryView = cell.accessoryUncheck
}
return cell
}
FilterData
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text)
let array = (teamArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
Team Objects
class Team{
var id: Int!
var name: NSString!
var shortname: NSString!
init(id: Int, name:NSString, shortname: NSString) {
self.id = id
self.name = name
self.shortname = shortname
}
}
The objects in the teamArray don't have a SELF property. You can't use SELF to search in all the properties of the object at once. You have to give the name of the property, and if you want to search in more than one you have to add all those properties to the predicate.
I would think it's enough for you to search in the name property like so:
let searchPredicate = NSPredicate(format: "name CONTAINS[c] %#", searchController.searchBar.text)
If you need in more properties you do like this:
let searchPredicate = NSPredicate(format: "name CONTAINS[c] %# OR shortname CONTAINS[c] %#", searchController.searchBar.text, searchController.searchBar.text)
Can you post the definition of your Team object, and any sub-objects that it contains? (TeamData).
Also indicate where you expect the search text to appear in your team object.
I haven't used NSPRedicate a lot, but my understanding of the CONTAINS comparison is that it checks an individual field to see if it contains a substring. I don't think it will check all fields of the objects you're searching. That seems like what you're expecting.
let searchPredicate = NSPredicate(format: "name contains[c] %#", searchWord)
self.filteredArray = self.array.filteredArrayUsingPredicate(searchPredicate)