0

Currently we iterate string as below:

let greeting = "Hello"
for (intIndex, char) in greeting.enumerated() {
    let currentIndex = greeting.index(greeting.startIndex, offsetBy: intIndex)
    let indexAfterCurrentIndex = greeting.index(after: currentIndex)
    print(greeting[indexAfterCurrentIndex...])
}

I feel writing below code is redundant.

let currentIndex = greeting.index(greeting.startIndex, offsetBy: intIndex)

Is there other way to get directly "String.Index" while iterating?

Something like this

let greeting = "Hello"
for (stringIndex, char) in greeting.enumeratedXXX() {
    let indexAfterCurrentIndex = greeting.index(after: stringIndex)
    print(greeting[indexAfterCurrentIndex...])
}
1

4 Answers 4

3

There is no built-in functionality for this. You could wrap this in a custom iterator, but then you only encapsulate the same kind of computation in a different place, so that's not an answer :)

Code Complexity

However, you can improve performance of your current code:

greeting.index(greeting.startIndex, offsetBy: intIndex)
  • This will calculate the index from startIndex to the resulting index for every loop iteration.
  • The index calculation with index(_:offsetBy:) is really just another loop itself, where it +1s each index. There's no O(1) way to "compute" the index; it is found out by a loop in O(n)

So your own outer loop is linear with O(n) for n iterations, one for every character.

Then computing the index with an inner loop means there are 1+2+3+4+5+6+...n = (n^2 + n)/2 iterations, where n is the intIndex in this case.

That means the algorithm has a complexity of *handwaiving* roundabout O(n + n^2). The quadratic part is problematic!

Better approach

You can get the complexity down to 2 operations per iteration, or O(2n). Just keep the previously computed index in memory and +1 yourself, avoiding a recomputation from scratch.

Here's the code:

let greeting = "Hello"
var index = greeting.startIndex
for char in greeting {
    let indexAfterCurrentIndex = greeting.index(after: index)
    print(greeting[indexAfterCurrentIndex...])
    index = indexAfterCurrentIndex
}

Still not a simple and built-in solution, but you can just as well wrap this more efficient algorithm and off you go!

extension String {
    func forEachCharacterWithIndex(iterator: (String.Index, Character) -> Void) {
        var currIndex = self.startIndex
        for char in self {
            iterator(currIndex, char)
            currIndex = self.index(after: currIndex)
        }
    }
}

let greeting = "Hello"
greeting.forEachCharacterWithIndex { (index, char) in
    let indexAfterCurrentIndex = greeting.index(after: index)
    print(greeting[indexAfterCurrentIndex...])
}
Sign up to request clarification or add additional context in comments.

Comments

2

If you need the string indices then you can enumerate greeting.indices:

let greeting = "Hello"
for index in greeting.indices {
    // ...
}

If you need each character together with its index then you can enumerate the string and the indices in parallel:

let greeting = "Hello"
for (char, currentIndex) in zip(greeting, greeting.indices) {
    let indexAfterCurrentIndex = greeting.index(after: currentIndex)
    print(char, "-", greeting[indexAfterCurrentIndex...])
}

Output:

H - ello
e - llo
l - lo
l - o
o -

A simpler variant would be

let greeting = "Hello"
for (char, nextIndex) in zip(greeting, greeting.indices.dropFirst()) {
    print(char, "-", greeting[nextIndex...])
}

which produces almost the same result, only without the last character/index pair:

H - ello
e - llo
l - lo
l - o

1 Comment

thanks, indices is pretty useful for something like zip(mutableSelf.indices, mutableSelf).compactMap { index, char in ... }
0
func mergeAlternately (word1: String, word2: String){
    var count = 0;
    var result = "";
    while (count < word1.count || count < word2.count ) {
        let index = word1.index(word1.startIndex, offsetBy: count) // 0-based index, 2nd position for 'l'
        let character = word1[index]
        result += String(character);
        print(result) // Output: "l"`enter code here`
       count = count + 1
    }
}

Comments

-1

Why not increment the currentIndex by 1 ?

let greeting = "Hello"
for (stringIndex, char) in greeting.enumerated() {
    let currentIndex = stringIndex
    let nextIndex = currentIndex + 1
    print(nextIndex)
}

3 Comments

You cannot increment a String.Index with + and an Int
@ctietze enumerated() doesn't return an Index. It returns the element offset (an Int)
@LeoDabus Right, but that does not help when the OP wants to work with substrings (see title). greeting.index(greeting.startIndex, offsetBy: intIndex + 1) would be 1 line shorter, but it's the same problem

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.