多个过滤器用于同一个searchBar,并忽略Swift 5中的order



在我的应用程序中,我构建了一个搜索栏,可以忽略顺序搜索多个单词在Swift 5 中搜索忽略顺序的多个单词

导入基础

class Clinic {
var id = ""
var name = ""
var address = ""
var specialty1 = ""
var specialty2 = ""
}

在我的文本DidChange中,我有

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let searArr = searchText.lowercased().components(separatedBy: " ")
clinicsSearch = clinics.filter { item in
let lowName = item.name.lowercased()
let lowSp1 = item.specialty1.lowercased()
let lowSp2 = item.specialty2.lowercased()
let res  = searArr.filter {
lowName.contains($0) ||    
lowSp1.contains($0) || 
lowSp2.contains($0) 
}
return !res.isEmpty
} // Credits to @Sh_Khan

有了上面的代码,我可以搜索任何键入的单词,独立于顺序,这会带来所有符合任何条件的条目。

我现在正试图从上面的内容中构建一个变体。。。用例是

  • 假设总共有10个诊所(第一诊所、第二诊所、第三诊所、第四诊所等等(。其中3家是骨科医生,诊所名称分别为第一诊所、第二诊所和第三诊所。所以,我可以开始键入Orthopedist(这将过滤到3个名称(。我现在想要我键入的下一个单词来过滤我所选择的内容(例如,键入First将把已经显示的3个诊所过滤为符合此附加标准的诊所(。在我的例子中,这将把列表减少为一个诊所。

我已尝试通过&我以为这已经足够了,但我有了一个意想不到的行为。当我开始打字时,它只考虑第一个字母,然后从过滤器中删除所有内容。对于上面的例子,如果我键入"F",它会带来First Clinic,但如果我输入"Fi",它什么也不会带来。我还考虑过创建多个过滤变量,但这可能会将键入的单词数量限制为我创建的变量数量。

我想要的可行吗?

您可以使用递归搜索,其中第一次搜索的匹配项将传递给下一次搜索,下一次只搜索下一个搜索项的匹配项。对每个搜索项继续执行此操作,然后只返回剩余的匹配项。

我把它放在一个可以在操场上跑步的例子中,这样你就可以玩一玩,看看它是如何工作的。

你可以(也应该(添加一个优化,这样如果没有剩余的匹配项,搜索就会提前终止,但我将把它留给你练习,因为它将帮助你了解递归搜索是如何工作的。如果您需要帮助,请在评论中进行讨论。

我还删除了一些不需要的临时变量(小写版本(,使用递归意味着只使用一个过滤器。

import UIKit
class Clinic: CustomStringConvertible {
var id = ""
var name = ""
var address = ""
var specialty1 = ""
var specialty2 = ""
var description: String {
get { return "Name: (name) Sp1: (specialty1) Sp2: (specialty2)"}
}
init(data: [String]) {
id = data[0]
name = data[1]
address = data[2]
specialty1 = data[3]
specialty2 = data[4]
}
}
var clinics = [
Clinic(data: ["1","First Clinic","First Street","head","feet"]),
Clinic(data: ["2","Second Clinic","Second Street","legs","feet"]),
Clinic(data: ["3","Third Clinic","Third Street","head","legs"])
]
// recursive search: search remaining clinics with remaining searches
func search(searchClinics: [Clinic], searchArr: [String]) -> [Clinic] {
var searchArr = searchArr
// base case - no more searches - return clinics found
if searchArr.count == 0 {
return searchClinics
}
// itterative case - search clinic with next search term and pass results to next search
let foundClinics = searchClinics.filter { item in
item.name.lowercased().contains(searchArr[0]) ||
item.specialty1.lowercased().contains(searchArr[0]) ||
item.specialty2.lowercased().contains(searchArr[0])
}
// remove completed search and call next search
searchArr.remove(at: 0)
return search(searchClinics: foundClinics, searchArr: searchArr)
}
let searchArr = "cli le".lowercased().components(separatedBy: " ")
let foundClinics = search(searchClinics: clinics, searchArr: searchArr)
foundClinics.map() {print($0)}
// Outputs:
// Name: Second Clinic Sp1: legs Sp2: feet
// Name: Third Clinic Sp1: head Sp2: legs

您应该预先计算小写字母。您可能还应该在后台执行此操作,并将结果发送回主队列。

性能问题除外:

当前的逻辑是,如果搜索词中的任何单词都在任何搜索目标中,则为true。

如果ALL搜索词中的所有单词都在任何搜索目标中,则需要将其更改为true。

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let terms = searchText.lowercased().components(separatedBy: " ")
clinicsSearch = clinics.filter { item in
let lowName = item.name.lowercased()
let lowSp1 = item.specialty1.lowercased()
let lowSp2 = item.specialty2.lowercased()
let targetsContainTerm: [Bool] = searArr.map {
lowName.contains($0) ||
lowSp1.contains($0) ||
lowSp2.contains($0)
}
return targetsContainTerm.allSatisfy { $0 = true}
} // Credits to @Sh_Khan

最新更新