简体   繁体   English

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

[英]How to filter a struct containing a string and an array?

Basically this is a struct object holding a country and its ports 基本上,这是一个包含一个国家及其港口的结构对象

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

These are the arrays holding each country mapped to its ports. 这些是将每个国家/地区映射到其港口的阵列。 The filtered array should hold filtered results either by country or ports. 过滤后的数组应按国家或港口保存过滤后的结果。

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

Currently the function below successfully produces a filtered result using countries as search key 当前,以下功能使用国家/地区作为搜索键成功生成了过滤结果

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

Now my problem is I wish to get a filtered result either by using a port or country as search key. 现在,我的问题是我希望通过使用端口或国家/地区作为搜索关键字来获得过滤结果。 I have tried my possible best but can't seem to get desirable results using ports as the search key. 我已尽力而为,但使用端口作为搜索键似乎无法获得理想的结果。 Improvements on my current method would be much appreciated. 我当前方法的改进将不胜感激。 Answers in Objective-C are welcomed as well. 也欢迎在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 ------------------------------- -------------------------------- UDPATE ----------------- --------------

At the moment typing "vlone" as a search string returns both "vlone" and "vlore". 目前,输入“ vlone”作为搜索字符串会同时返回“ vlone”和“ vlore”。 Vlone and Vlore are both ports in Albania Vlone和Vlore都是阿尔巴尼亚的港口

Result yielded using vlone as search string 使用vlone作为搜索字符串产生的结果

However the desired output should only include "vlone" and leave out every other port in Albania because the user was specific with their query. 但是,所需的输出应仅包括“ vlone”,而忽略阿尔巴尼亚的所有其他端口,因为用户是特定于其查询的。

2nd scenario would be using "barcelona" as search key. 第二种情况是使用“巴塞罗那”作为搜索关键字。 It returns a result containing every port in Spain however the desired output should only contain Barcelona. 它返回一个包含西班牙每个端口的结果,但是所需的输出应仅包含巴塞罗那。

Results using barcelona as search key 使用巴塞罗那作为搜索关键字的结果

If you want to filter the array to find any entry that has a matching country or port, then you can do: 如果要过滤数组以查找具有匹配的国家或地区的任何条目,则可以执行以下操作:

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

Let's break this down. 让我们分解一下。 At the top level is the call to filter on the countryAndPortsArray array. 顶层是对countryAndPortsArray数组进行filter的调用。 The filter consists of two parts. 过滤器由两部分组成。 1) checking to see if the country contains the search text, and 2) checking to see if any of the ports contain the search text. 1)检查该国家/地区是否包含搜索文字,以及2)检查任何端口是否包含搜索文字。

The first check is done with: 第一步检查完成:

$0.country?.lowercased().range(of: searchKey) != nil

which is the part you already had. 这是您已经拥有的部分。

The second check is done with: 第二项检查通过以下方式完成:

!($0.ports?.filter {
    $0.lowercased().range(of: searchKey) != nil
}.isEmpty ?? true)

Since ports is an array, a filter is being used to see if any of the ports contains the search text. 由于ports是一个数组,因此使用过滤器来查看是否有任何端口包含搜索文本。 The isEmpty is checking if the resulting filter list of matching ports is empty. isEmpty正在检查匹配端口的结果过滤器列表是否为空。 Since ports is optional, the use of ?? true 由于ports是可选的,因此使用?? true ?? true means to act as if the list of matching ports is empty. ?? true表示充当匹配端口列表为空。 The ! ! negates the results. 否定结果。 So if the matching list is empty (or ports is nil ), the true is negated to false indicating that there are no matching ports. 因此,如果匹配列表为空(或portsnil ),则将true否定为false表示没有匹配的端口。

The two checks are combined using the standard logical "or" operator || 使用标准逻辑“或”运算符||两个检查组合在一起 meaning only one of the two checks needs to be true for the top level filter to match that record. 这意味着对于顶层过滤器以匹配该记录,仅需要两项检查中的一项为真。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM