André Fernandes
André Fernandes

Reputation: 69

Search for multiple words ignoring the order in Swift 5

In my app I built a SearchBar which, for a single word, works perfectly fine.

import Foundation

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

In my textDidChange I have

var clinics: [Clinic] = [] // Clinics Data Structure
var clinicsSearch: [Clinic] = [] // Filtered Clinics

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {

    // works for one word (either in name or specialty1 or specialty2)
    clinicsSearch = clinics.filter { $0.name.lowercased().contains(searchText.lowercased()) ||
        $0.specialty1.lowercased().contains(searchText.lowercased()) ||
        $0.specialty2.lowercased().contains(searchText.lowercased())
    }
    searching = true
    tableView.reloadData()
}

With the func above, if I start typing something, it will bring all results that meet the criteria (if the typed word is found in the Name OR Specialty1 OR Specialty2).

I now started to look to improve my code and want to implement an option where the user could type words in different order like: Name SPACE specialty(1or2), specialty(1or2) SPACE Name etc and the app would search all 3 fields for all typed words. Order independent.

I found here something very similar to what I'm looking for but I couldn't ask there as I'm below 50 reputation Search for multiple words ignoring the order in Swift

The main difference from the solution presented there is that they use basic a array in the example

let filterArray = ["Big green bubble", "Red bubble", "A bubble in green", "Small green bubble", "This bubble is green"]

That is different from mine, that is based in the Class Clinic above. I tried to adapt their code to my case as below

let textString = searchText.lowercased()
let words = textString.components(separatedBy: " ")
clinicsSearch = clinics.map { $0.name.lowercased() }.filter { string in words.allSatisfy { string.components(separatedBy: " ").contains($0) } }

But I get several errors like Value of type 'Clinic' has no member 'lowercased' or Value of type 'Clinic' has no member 'components' (there are others as well) that I don't know how to fix.

Any help is greatly appreciated.

Thanks

Upvotes: 0

Views: 769

Answers (1)

Shehata Gamal
Shehata Gamal

Reputation: 100503

You can try

let searArr = searchText.lowercased().components(separatedBy: " ")
clinicsSearch = clinics.filter { item in
                                  let res  = searArr.filter { item.name.lowercased().contains($0) ||    
                                  item.specialty1.lowercased().contains($0) || 
                                  item.specialty2.lowercased().contains($0) 
                }
   return !res.isEmpty
}

It could be better to do

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
}

Upvotes: 2

Related Questions