Searching through objects in array swift - ios

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)

Related

Link arrays for UISearchController in Swift

I m using a UISearchController to let the user search though my data (these comes from XML and been parsed to arrays), now i want to add more then one UILabel into my Cell, but these UILabel Information comes from different arrays.
As soon as i update my Search, the 2. array does not get sorted the same way like the first does.
AP = ["FIRST1","FIRST2","FIRST3","FIRST4","FIRST5"]
APSecound = ["SEC1","SEC2","SEC3","SEC4","SEC5"]
var filteredTableDataAP = [String]()
var filteredTableDataAPSecound = [String]()
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableDataIcao.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (AP as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableDataAP = array as! [String]
//i think here i some how could set index of "filteredTableDataAP = filteredTableDataAPSecound" so they been filtered like the same shema
self.tableView.reloadData()
}
Override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCellSearch", forIndexPath: indexPath) as! CustomTableViewCellSearch
if (self.resultSearchController.active) {
cell.APLB.text = filteredTableDataAP[indexPath.row]
cell.APSecoundLB.text = filteredTableDataAPSecound[indexPath.row]
return cell
}
else {
cell.APLB.text = AP[indexPath.row]
cell.APSecoundLB.text = APSecound[indexPath.row]
return cell
}
}
On first run without searching the cell looks fine like
(FIRST1)(SEC1)
(FIRST2)(SEC2)
(FIRST3)(SEC3)
(FIRST4)(SEC4)
But once i search for "4" the cell looks like
(FIRST4)(SEC1)
is there any easy way to "keep the position of the second array like the one of the first"
The way i have used is below
you have two array :
AP = ["FIRST1","FIRST2","FIRST3","FIRST4","FIRST5"]
APSecound = ["SEC1","SEC2","SEC3","SEC4","SEC5"]
var getIndex = 0
if (self.SearchController.active) // check if SearchController isactive
{
//This will get the index of main array
getIndex = AP.indexOf(filteredsearchPost[indexPath.row])!
//According to example index should be 1 for "FIRST2"
. cell.APSecoundLB.text = APSecound[getIndex]
//set it with the retrived index and its done
//it will print "SEC2"
}else {
getIndex = indexPath.row
cell.APLB.text = AP[getIndex]
cell.APSecoundLB.text = APSecound[getIndex]
//Do the else part here
}
you just filtered the AP not filtered APSecound
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (AP as NSArray).filteredArrayUsingPredicate(searchPredicate)
let arraySecond = (APSecound as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableDataAP = array as! [String]
filteredTableDataAPSecound = arraySecond as! [String]
or you can use filteredResault: [[String: String?]] , [String: String?] is the Dictionary, filteredResault is Dictionary array. dict like ["AP": "FIRST1", "APSecound": "SEC1"]

How to search inside of Dictionary in Swift

In my UITableView I have cells with avatar image and name surname(UIImage and Label). And I want to search inside of my UITableView. So, when I type my user's name or surname it would show user's name surname with the avatar image.
Now, I can search just inside of an array with the next code:
var rosters = [String: UIImage]()
var displayNames = [String]()
var filteredTableData = [String]()
// Here I remove all elements from the array and add to it user display name and avatar
rosters.removeAll(keepCapacity: false)
for user in OneRoster.buddyList.fetchedObjects! {
var roster = OneRoster.userFromRosterForJID(jid: user.jidStr)
var image = OneChat.sharedInstance.xmppvCardAvatarModule?.photoDataForJID(roster?.jid)
rosters[user.displayName] = UIImage(data: image!)!
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> onlineUserCell {
let cell:onlineUserCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! onlineUserCell
if (self.resultSearchController.active) {
cell.username.text = filteredTableData[indexPath.row]
//cell.avatarImage.image = images[indexPath.row]
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
displayNames.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
for (name, image) in rosters {
displayNames.append(name)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (displayNames as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
}
let array = (displayNames as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
So, this code searches just among name surname, without avatar image. I thought, that I can use dictionary, as:
var users = [UIImage: String]()
and later search by String and show as UIImage and String together in the cell. But I searched a lot and couldn't found how to search, that later I can search by String and to get result with UIImage, too.
Any ideas?
Searching the dictionary could be done by just accessing the values from the dictionary and searching them like
var users = [UIImage: String]()
let values = Array(users.values)
However this use of dictionary seems a bit wrong as you will never look up a value based on the UIImage I guess.
What you should use instead then would be a tuple or a class that holds both UIImage and String
var users = [(image:UIImage,name:String)]()
you can then simply filter the values like
users.filter { (element:(image:UIImage, name:String)) -> Bool in
return element.name.containsString(searchfrase)
}

Filter by array of Objects searchController

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.

Swift Array of objects filtering

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.

Remove cells when searchBar text is above 0

i've created a tableView which i first did set to that it should use the filtered array if the searchController was active. However i want to first do it when the searchBar contain more than 0 characters. However this does not seem to work. Here is what i've done so far in cellForRowAtIndexPath
if (count(self.teamSearchController.searchBar.text) > 0) {
team = filteredTableData[indexPath.row] as Team
cell.textLabel?.text = team.name as String
} else {
team = self.teamArray[indexPath.row] as Team
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Light", size: 20)
cell.textLabel?.text = team.name as String
}
updateSearchResultsForSearchController
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "name CONTAINS[c] %# OR shortname CONTAINS[c] %#", searchController.searchBar.text, searchController.searchBar.text)
let array = (teamArray as NSArray).filteredArrayUsingPredicate(searchPredicate) as! [Team]
filteredTableData = array
self.tableView.reloadData()
}
You should do the checking for length of search text in updateSearchResultsForSearchController
func updateSearchResultsForSearchController(searchController: UISearchController)
{
if searchController.searchBar.text == "" {
filteredTableData = self.teamArray
} else {
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "name CONTAINS[c] %# OR shortname CONTAINS[c] %#", searchController.searchBar.text, searchController.searchBar.text)
let array = (teamArray as NSArray).filteredArrayUsingPredicate(searchPredicate) as! [Team]
filteredTableData = array
}
self.tableView.reloadData()
}
Update the code in cellForRowAtIndexPath as shown below, just have a check if the searchController is active or not
if (self.teamSearchController.active) {
team = filteredTableData[indexPath.row] as Team
} else {
team = self.teamArray[indexPath.row] as Team
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Light", size: 20)
}
cell.textLabel?.text = team.name as String
I think it is not good design to query the search bar in every table view cell. Instead, have only one array that the table view displays.
Whenever the search bar contents change, change the content of the array to be displayed in the table to the filtered array. Handle your edge case in the update method. I think the if statement you wrote should work fine.

Resources