Vinit Ingale
Vinit Ingale

Reputation: 401

Looking for the best way to filter text from string in Swift

I have an array of Strings in ascending sorted order. I want to filter char/text in that array and get results from searched text letter first and then the rest. I am looking for the simplest way to do this job.                  Example:

Var array = ["Anand", "Ani", "Dan", "Eion", "Harsh", "Jocab", "Roshan", "Stewart"]

and search text is "R"

Output should be:

Var outArray = ["Roshan", "Harsh", "Stewart"]

Upvotes: 1

Views: 1618

Answers (3)

Sweeper
Sweeper

Reputation: 272725

One way to do this is to first map the strings to a tuple containing the index of the search text in string, and the string itself. Then sort by the index, then map the tuples back to the strings.

let array = ["Anand", "Ani", "Dan", "Eion", "Harsh", "Jocab", "Roshan", "Stewart"]
let searchText = "R"
// compactMap acts as a filter, removing the strings where string.index(of: searchText, options: [.caseInsensitive]) returns nil
let result = array.compactMap { string in string.index(of: searchText, options: [.caseInsensitive]).map { ($0, string) } }
                    .sorted { $0.0 < $1.0 }.map { $0.1 }

The index(of:options:) method is taken from this answer here.

For Swift 4.x:

extension StringProtocol where Index == String.Index {
    func index(of string: Self, options: String.CompareOptions = []) -> Index? {
        return range(of: string, options: options)?.lowerBound
    }
    func endIndex(of string: Self, options: String.CompareOptions = []) -> Index? {
        return range(of: string, options: options)?.upperBound
    }
    func indexes(of string: Self, options: String.CompareOptions = []) -> [Index] {
        var result: [Index] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...].range(of: string, options: options) {
                result.append(range.lowerBound)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
    func ranges(of string: Self, options: String.CompareOptions = []) -> [Range<Index>] {
        var result: [Range<Index>] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...].range(of: string, options: options) {
                result.append(range)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
}

Upvotes: 2

Coder ACJHP
Coder ACJHP

Reputation: 2224

let words = ["Anand", "Ani", "Dan", "Eion", "Harsh", "Jocab", "Roshan", "Stewart"]
let keyword = "r"
let result = words.filter { $0.contains(keyword) }
    .sorted { ($0.hasPrefix(keyword) ? 0 : 1) < ($1.hasPrefix(keyword) ? 0 : 1) }

Upvotes: 0

PGDev
PGDev

Reputation: 24341

Use filter on array to filter all the Strings that contain the searchText, i.e.

let array = ["Anand", "Ani", "Dan", "Eion", "Harsh", "Jocab", "Roshan", "Stewart"]
let searchText = "R"
let filteredArray = array.filter({$0.lowercased().contains(searchText.lowercased())})
let sortedArray = filteredArray.sorted { (str1, str2) -> Bool in
    if let index1 = str1.lowercased().firstIndex(of: Character(searchText.lowercased())), let index2 = str2.lowercased().firstIndex(of: Character(searchText.lowercased())) {
        return index1 < index2
    }
    return false
}
print(sortedArray) //["Roshan", "Harsh", "Stewart"]

Upvotes: 1

Related Questions