I have a standard tableview app that displays information from a data model array that uses a custom class. There is a UISearchController
to filter the data.
A simplified version of the custom class is shown. I use a computed property for a string that is checked when searching the data.
class myObject: NSObject {
// Inherits from NSObject as I use NSCoding for persistence
var id: String = UUID().uuidString // These are unique for each item
var name: String = ""
var type: String = ""
var batch: String = ""
var searchableString: String {
// This is a concatenation of some of the properties
return self.name + " " + self.type + " " + self.batch
}
// etc...
}
Every time the user types a new character into the search bar of the UISearchController
, the following method is called, which filters the main data array into a filteredItems
array. Initially, I used a very basic form where the entire search text had to match.
func searchItems() {
if let searchText = searchController.searchBar.text {
filteredItems = allItems.filter { item in
return item.searchableString.lowercased().contains(searchText.lowercased())
}
}
}
filteredItems
is then displayed in the tableview.
I wanted to improve this so that each word typed in the search bar is searched for separately. I tried this, but of course it filters using OR logic (any word can match).
func searchItems() {
if let searchText = searchController.searchBar.text?.split(separator: " ") {
// Need to convert substring from split back to string
let searchArray = searchText.map() { substring in
String(substring)
}
filteredItems = allItems.filter { item in
return searchArray.contains(where: drug.searchableString.lowercased().contains)
}
}
}
In order to get the desired AND logic (every word typed must match), I had to use this.
func searchItems() {
if let searchText = searchController.searchBar.text?.split(separator: " ") {
// Need to convert substring from split back to string
let searchArray = searchText.map() { substring in
String(substring)
}
filteredItems = allItems.filter { item in
for word in searchArray {
if !item.searchableString.lowercased().contains(word) {
return false
}
}
return true
}
}
}
This works as intended, but it does not seem elegant. Is there a better way to filter the array allItems
to filteredItems
based on matching text in those String
properties of the objects in my data model?