简体   繁体   中英

How to use firstIndex in Switft to find all results

I am trying to split a string into an array of letters, but keep some of the letters together. (I'm trying to break them into sound groups for pronunciation, for example). So, for example, all the "sh' combinations would be one value in the array instead of two.

It is easy to find an 's' in an array that I know has an "sh" in it, using firstIndex. But how do I get more than just the first, or last, index of the array?

The Swift documentation includes this example:

let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
if let i = students.firstIndex(where: { $0.hasPrefix("A") }) {
    print("\(students[i]) starts with 'A'!")
}
// Prints "Abena starts with 'A'!"

How do I get both Abena and Akosua (and others, if there were more?)

Here is my code that accomplishes some of what I want (please excuse the rather lame error catching)

let message = "she sells seashells"
var letterArray = message.map { String($0)}
var error = false

while error == false {
    if message.contains("sh") {
        guard let locate1 = letterArray.firstIndex(of: "s") else{
            error = true
            break }
        let locate2 = locate1 + 1
        //since it keeps finding an s it doesn't know how to move on to rest of string and we get an infinite loop
        if letterArray[locate2] == "h"{
            letterArray.insert("sh", at: locate1)
            letterArray.remove (at: locate1 + 1)
            letterArray.remove (at: locate2)}}
    else { error = true }}
print (message, letterArray)

Instead of first use filter you will get both Abena and Akosua (and others, if there were more?)

   extension Array where Element: Equatable {
    func allIndexes(of element: Element) -> [Int] {
        return self.enumerated().filter({ element == $0.element }).map({ $0.offset })
    }
}

You can then call

letterArray.allIndexes(of: "s") // [0, 4, 8, 10, 13, 18]

You can filter the collection indices:

let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
let indices = students.indices.filter({students[$0].hasPrefix("A")})
print(indices)  // "[1, 4]\n"

You can also create your own indices method that takes a predicate:

extension Collection {
    func indices(where predicate: @escaping (Element) throws -> Bool) rethrows -> [Index] {
        try indices.filter { try predicate(self[$0]) }
    }
}

Usage:

let indices = students.indices { $0.hasPrefix("A") }
print(indices)  // "[1, 4]\n"

or indices(of:) where the collection elements are Equatable :

extension Collection where Element: Equatable {
    func indices(of element: Element) -> [Index] {
        indices.filter { self[$0] == element }
    }
}

usage:

let message = "she sells seashells"
let indices = message.indices(of: "s")
print(indices)

Note: If you need to find all ranges of a substring in a string you can check this post .

Have fun!

["Kofi", "Abena", "Peter", "Kweku", "Akosua"].forEach {
    if $0.hasPrefix("A") {
        print("\($0) starts with 'A'!")
    }
}

If you really want to use the firstIndex method, here's a recursive(:) implementation just for fun :D

extension Collection where Element: Equatable {

    /// Returns the indices of an element from the specified index to the end of the collection.
    func indices(of element: Element, fromIndex: Index? = nil) -> [Index] {
        let subsequence = suffix(from: fromIndex ?? startIndex)
        if let elementIndex = subsequence.firstIndex(of: element) {
            return [elementIndex] + indices(of: element, fromIndex: index(elementIndex, offsetBy: 1))
        }
        return []
    }

}

Recursions

Given n instances of element in the collection, the function will be called n+1 times (including the first call).

Complexity

Looking at complexity, suffix(from:) is O(1), and firstIndex(of:) is O(n). Assuming that firstIndex terminates once it encounters the first match, any recursions simply pick up where we left off. Therefore, indices(of:fromIndex:) is O(n), just as good as using filter . Sadly, this function is not tail recursive... although we can change that by keeping a running total.

Performance

[Maybe I'll do this another time.]

Disclaimer

Recursion is fun and all, but you should probably use Leo Dabus' solution.

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