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...])
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Faisal Ikwal
  • 703
  • 3
  • 8
  • 25

3 Answers3

2

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...])
}
ctietze
  • 2,805
  • 25
  • 46
1

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
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
-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)
}
SwatGuard
  • 55
  • 7
  • You cannot increment a `String.Index` with `+` and an `Int` – ctietze Jun 20 '19 at 13:57
  • 1
    @ctietze enumerated() doesn't return an Index. It returns the element offset (an Int) – Leo Dabus Jun 20 '19 at 19:51
  • @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 – ctietze Jun 21 '19 at 05:45