如何过滤包含字符串和数组的结构?

How to filter a struct containing a string and an array?

基本上这是一个包含国家及其港口的结构对象

struct countryAndPorts {
   var country: String?
   var ports: [String]?
}

这些是包含映射到其端口的每个国家/地区的数组。过滤后的数组应按国家或港口保存过滤结果。

var countryAndPortsArray = [countryAndPorts]()
var filteredArray = [countryAndPorts]()

目前,以下函数成功生成了使用国家/地区作为搜索关键字的过滤结果

func filteredContent(searchKey: String) {
    filteredArray = countryAndPortsArray.flatMap{ [=12=] }.filter{[=12=].country?.lowercased().range(of: searchKey) != nil }
}

现在我的问题是我希望通过使用端口或国家/地区作为搜索关键字来获得过滤结果。我已尽力而为,但似乎无法使用端口作为搜索键获得理想的结果。对我当前方法的改进将不胜感激。也欢迎 Objective-C 中的回答。

//Just incase it's of any use, these are my UITableView delegates 
extension SearchDealsViewController: UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let count = filteredArray[section].ports?.count {
        return count
    }
    return 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    if let port = filteredArray[indexPath.section].ports?[indexPath.row] {
        cell.textLabel?.text = port
    }
    cell.textLabel?.font = UIFont.systemFont(ofSize: 10)
    return cell
}

func numberOfSections(in tableView: UITableView) -> Int {
    return filteredArray.count
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    portsFromTextField.text = filteredArray[indexPath.section].ports?[indexPath.row]
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let header = tableView.dequeueReusableCell(withIdentifier: "header")
    let tapSectionHeaderGesture = UITapGestureRecognizer(target: self, action: #selector(getCountryText(sender:)))
    tapSectionHeaderGesture.numberOfTouchesRequired = 1
    tapSectionHeaderGesture.numberOfTapsRequired = 1
    header?.addGestureRecognizer(tapSectionHeaderGesture)

    if header != nil {
        header?.textLabel?.textColor = THEME_COLOUR
        header?.textLabel?.font = UIFont.boldSystemFont(ofSize: 12)
        header?.textLabel?.text = filteredArray[section].country
    }

    return header
}

func getCountryText(sender: UITapGestureRecognizer) {
    if let country = sender.view as? UITableViewCell {
        portsFromTextField.text = country.textLabel?.text
    }
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 30
}

-------------------------------- UDPATE ------ ----------------------

目前正在输入 "vlone" 作为搜索字符串 returns "vlone" 和 "vlore"。 Vlone 和 Vlore 都是阿尔巴尼亚的港口

Result yielded using vlone as search string

然而,所需的输出应仅包括 "vlone",而忽略阿尔巴尼亚的所有其他港口,因为用户的查询是特定的。

第二种情况是使用 "barcelona" 作为搜索关键字。它 returns 结果包含西班牙的每个港口,但是所需的输出应该只包含巴塞罗那。

Results using barcelona as search key

如果你想过滤数组以找到任何具有匹配国家或端口的条目,那么你可以这样做:

func filteredContent(searchKey: String) {
    filteredArray = countryAndPortsArray.filter {
        [=10=].country?.lowercased().range(of: searchKey) != nil ||
        !([=10=].ports?.filter {
            [=10=].lowercased().range(of: searchKey) != nil
        }.isEmpty ?? true)
    }
}

我们来分解一下。顶层是对 countryAndPortsArray 数组上的 filter 的调用。过滤器由两部分组成。 1) 检查国家是否包含搜索文本,以及 2) 检查端口是否包含搜索文本。

第一次检查完成于:

[=11=].country?.lowercased().range(of: searchKey) != nil

这是你已经拥有的部分。

第二次检查完成:

!([=12=].ports?.filter {
    [=12=].lowercased().range(of: searchKey) != nil
}.isEmpty ?? true)

由于 ports 是一个数组,过滤器被用来查看是否有任何端口包含搜索文本。 isEmpty 正在检查结果匹配端口的过滤器列表是否为空。由于 ports 是可选的,因此使用 ?? true 意味着匹配端口列表就好像是空的一样。 ! 否定结果。因此,如果匹配列表为空(或 portsnil),则 true 取反为 false,表示没有匹配端口。

这两项检查使用标准逻辑 "or" 运算符组合 ||,这意味着只有两项检查中的一项为真,顶级过滤器才能匹配该记录。