简体   繁体   中英

Filtering a struct object containing a string and array of strings

I have a struct object holding a mapping of ports to their respective countries.

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

I have an array var countryAndPortsArray = [countryAndPorts]() that holds countryAndPort object for every country.

I also have an array var filteredArray = [countryAndPorts]() which holds filtered countryAndPortsArray objects depending on the user's search query

I am filtering countryAndPortsArray with this function

func filteredContent(searchKey: String) {
    filteredArray = countryAndPortsArray.filter {
      !($0.ports?.filter{
           $0.range(of: searchKey, options: .caseInsensitive, range: 
           nil, locale: nil) != nil || 
           $0.localizedCaseInsensitiveCompare(searchKey) == .orderedSame
          }.isEmpty ?? true) || 
       $0.country?.localizedCaseInsensitiveContains(searchKey) ?? true
}

My problem is the result of filteredArray contains every port within the same country regardless the user's search key matches a particular port.

For example, if a user searches for "Paris", the filteredArray will contain every port in France. If a user searches "barcelona", the filteredArray will contain every port in Spain.

The desired result should only contain ports matching "barcelona" or "paris" and not every other port within the same country.

UPDATE

Use Case:

var france = countryAndPorts(country: "France", ports: 
["monaco","paris","ajaccio","lyon"])

var spain = countryAndPorts(country: "Spain", ports: ["barcelona", 
"madrid", "catalan"])

var countryAndPortsArray = [france,spain]

if i search countryAndPortsArray for barcelona, it returns ["barcelona", "madrid", "catalan"] . However i would want it to only return ["barcelona"]

here is my tableview delegates just incase it sheds more light to my question

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
}

I write solution but it is not perfect. But still it return expected result.

Firstly we could edit structure to fetch as preferred result.

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

    func getPort(_ withName: String) -> [String]? {
        return ports?.filter{$0.lowercased() == withName.lowercased()}
    }
}

Than out function to filter data. It is not perfect but it works it is possible to reduce amount of flatMap calls.

func fetch(port withName: String, from ports: [countryAndPorts]) -> [String]? {
    return ports.map{$0.getPort(withName)}.flatMap{$0}.filter{($0?.count)! > 0}.flatMap{$0}.first
}

Result:

fetch(port: "madrid", from: countryAndPortsArray) // ["madrid"]?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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