0

In Swift 3 SE-0065 changed the indexing model for Collections, wherein responsibility for index traversal is moved from the index to the collection itself. For example, instead of writing i.successor(), onehas to write c.index(after: i).

What this means in terms of accessing Strings at a certain index, is that instead of writing this:

let aStringName = "Bar Baz"
aStringName[aStringName.startIndex.advancedBy(3)]

...we now have to write this:

aStringName[aStringName.index(aStringName.startIndex, offsetBy: 3)]

This seems incredibly redundant, as aStringName is mentioned thrice. So my question is if there's any way to get around this (aside from writing an extension to String)?

6
  • 1
    I believe this very point was lifted as one of the disadvantages of the SE-0065 proposal, however somewhat redeemed by the fact that in many applications the collection aStringName could be referred to by self and in many applications, hence omitted. Commented Aug 11, 2016 at 13:08
  • 2
    Array(s.characters)[3] would be concise, but ineffective for long strings. s.characters.dropFirst(3).first is an alternative, but not really more nice. – Often you need the characters sequentially, and then you should enumerate them instead of indexing repeatedly. Commented Aug 11, 2016 at 13:13
  • @MartinR does the enumerated() method produce a lazy sequence for Strings? Because otherwise it seems quite expensive to enumerate the characters. Commented Aug 11, 2016 at 13:18
  • 2
    I am fairly sure that enumerated() produces a lazy sequence, and effectively yields all characters. Commented Aug 11, 2016 at 13:23
  • 1
    I have long ago got fed up of Swift's string access model. Most of the time, I can care less about "extended grapheme cluster" and just want to get the n-th character of a string. So I just extend String to add a subscript by Int Commented Aug 11, 2016 at 14:23

2 Answers 2

5

For future Googlers, I'm posting my String extension below. It allows you to access a string with Ints rather than the cumbersome Index:

extension String {
    subscript(index: Int) -> Character {
        let startIndex = self.index(self.startIndex, offsetBy: index)
        return self[startIndex]
    }

    subscript(range: CountableRange<Int>) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
        let endIndex = self.index(startIndex, offsetBy: range.count)
        return self[startIndex..<endIndex]
    }

    subscript(range: CountableClosedRange<Int>) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
        let endIndex = self.index(startIndex, offsetBy: range.count)
        return self[startIndex...endIndex]
    }

    subscript(range: NSRange) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: range.location)
        let endIndex = self.index(startIndex, offsetBy: range.length)
        return self[startIndex..<endIndex]
    }
}

let str = "Hello world"

print(str[0])                   // Get the first character
print(str[0..<5])               // Get characters 0 - 4, with a CountableRange
print(str[0...4])               // Get chacraters 0 - 4, with a ClosedCountableRange
print(str[NSMakeRange(0, 5)])   // For interacting with Foundation classes, such as NSRegularExpression
Sign up to request clarification or add additional context in comments.

Comments

0

It seems there's no neat or concise way to do this without writing an extension. A version of such an extension could look like this:

extension String {
  subscript(offset offset: Int) -> Character {
    return self[index(startIndex, offsetBy: offset)]
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.