27

The following was possible with Swift 2.2:

let m = "alpha"
for i in m.startIndex..<m.endIndex {
    print(m[i])
}
a
l
p
h
a

With 3.0, we get the following error:

Type 'Range' (aka 'Range') does not conform to protocol 'Sequence'

I am trying to do a very simple operation with strings in swift -- simply traverse through the first half of the string (or a more generic problem: traverse through a range of a string).

I can do the following:

let s = "string"
var midIndex = s.index(s.startIndex, offsetBy: s.characters.count/2)
let r = Range(s.startIndex..<midIndex)
print(s[r])

But here I'm not really traversing the string. So the question is: how do I traverse through a range of a given string. Like:

for i in Range(s.startIndex..<s.midIndex) {
    print(s[i])
}

10 Answers 10

31

You can traverse a string by using indices property of the characters property like this:

let letters = "string"
let middle = letters.index(letters.startIndex, offsetBy: letters.characters.count / 2)

for index in letters.characters.indices {

    // to traverse to half the length of string 
    if index == middle { break }  // s, t, r

    print(letters[index])  // s, t, r, i, n, g
}

From the documentation in section Strings and Characters - Counting Characters:

Extended grapheme clusters can be composed of one or more Unicode scalars. This means that different characters—and different representations of the same character—can require different amounts of memory to store. Because of this, characters in Swift do not each take up the same amount of memory within a string’s representation. As a result, the number of characters in a string cannot be calculated without iterating through the string to determine its extended grapheme cluster boundaries.

emphasis is my own.

This will not work:

let secondChar = letters[1] 
// error: subscript is unavailable, cannot subscript String with an Int
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @khundragpan. This is closer to what I was expecting as a solution but it's still not generic for a traversal over a given range. Similar to my comment for @dfri's post, how do I traverse over the Range(1,3) for example for a string "string" resulting in a traversal over t, r, i.
@p0lAris I believe @Khundragpan gave you a very good starting point. See the offsetBy as your start and a count for your range. Turning this into something that you can use is trivial.
I'm receiving: Type 'String?' has no subscript members
N.B. since characters was deprecated in Swift 4, you should now call indices directly on the string itself.
29

Another option is to use enumerated() e.g:

let string = "Hello World"    
for (index, char) in string.characters.enumerated() {
    print(char)
}

or for Swift 4 just use

let string = "Hello World"    
for (index, char) in string.enumerated() {
    print(char)
}

2 Comments

string.characters is deprecated
@NathanFiscaletti the question asked about Swift 3.0, nevertheless I've updated the answer
8

Use the following:

for i in s.characters.indices[s.startIndex..<s.endIndex] {
  print(s[i])
}

Taken from Migrating to Swift 2.3 or Swift 3 from Swift 2.2

Comments

6

Iterating over characters in a string is cleaner in Swift 4:

let myString = "Hello World"    
for char in myString {
    print(char)
}

3 Comments

But it returns a character, not a single letter as a string. It may be what you want, but it may not be.
What's the difference here between letter and character?
poor word choice, the difference is a character and a string of length 1
3

If you want to traverse over the characters of a String, then instead of explicitly accessing the indices of the String, you could simply work with the CharacterView of the String, which conforms to CollectionType, allowing you access to neat subsequencing methods such as prefix(_:) and so on.

/* traverse the characters of your string instance,
   up to middle character of the string, where "middle"
   will be rounded down for strings of an odd amount of
   characters (e.g. 5 characters -> travers through 2)  */
let m = "alpha"
for ch in m.characters.prefix(m.characters.count/2) {
    print(ch, ch.dynamicType)
} /* a Character
     l Character */

/* round odd division up instead */
for ch in m.characters.prefix((m.characters.count+1)/2) {
    print(ch, ch.dynamicType)
} /* a Character
     l Character 
     p Character */

If you'd like to treat the characters within the loop as strings, simply use String(ch) above.


With regard to your comment below: if you'd like to access a range of the CharacterView, you could easily implement your own extension of CollectionType (specified for when Generator.Element is Character) making use of both prefix(_:) and suffix(_:) to yield a sub-collection given e.g. a half-open (from..<to) range

/* for values to >= count, prefixed CharacterView will be suffixed until its end */
extension CollectionType where Generator.Element == Character {
    func inHalfOpenRange(from: Int, to: Int) -> Self {
        guard case let to = min(to, underestimateCount()) where from <= to else {
            return self.prefix(0) as! Self
        }
        return self.prefix(to).suffix(to-from) as! Self
    }
}

/* example */
let m = "0123456789"    
for ch in m.characters.inHalfOpenRange(4, to: 8) {
    print(ch)         /*  \                                   */
} /* 4                     a (sub-collection) CharacterView
     5
     6
     7 */

2 Comments

Thanks @dfri. This solution however doesn't do a generic traversal over a particular range index. For e.g. given a string = "string", I need to traverse over the Range(1, 3) and therefore, traversing over t, r & i. Maybe we also need to use suffix in there to get the first part of the range (not too sure).
@p0lAris added an extension example into my answer, allowing constructing a sub-CharacterView for a given CharacterView, where the former can naturally be traversed over as in the original answer, using e.g. for ... in.
2

The best way to do this is :-

 let name = "nick" // The String which we want to print.

  for i in 0..<name.count 
{
   // Operation name[i] is not allowed in Swift, an alternative is
   let index = name.index[name.startIndex, offsetBy: i]
   print(name[index])
}

for more details visit here

1 Comment

Please keep in mind that computing an ever-growing offset from startIndex gets slower with every iteration, with noticeable time consumption for huge strings (e.g. whole documents). It's more efficient to keep the last index around and add an offset of 1 to it.
2

Swift 4.2

Simply:

let m = "alpha"
for i in m.indices {
   print(m[i])
}

Comments

0

Swift 4:

let mi: String = "hello how are you?"
for i in mi {
   print(i)
}

1 Comment

That's an obsolete approach. The Swift String is now a collection. You can simply iterate through a string using the for-in loop: let string = "🤖😻👻💩" for char in string { print(char) }
0

To concretely demonstrate how to traverse through a range in a string in Swift 4, we can use the where filter in a for loop to filter its execution to the specified range:

func iterateStringByRange(_ sentence: String, from: Int, to: Int) {

    let startIndex = sentence.index(sentence.startIndex, offsetBy: from)
    let endIndex = sentence.index(sentence.startIndex, offsetBy: to)

    for position in sentence.indices where (position >= startIndex && position < endIndex) {
        let char = sentence[position]
        print(char)
    }

}

iterateStringByRange("string", from: 1, to: 3) will print t, r and i

Comments

0

When iterating over the indices of characters in a string, you seldom only need the index. You probably also need the character at the given index. As specified by Paulo (updated for Swift 4+), string.indices will give you the indices of the characters. zip can be used to combine index and character:

let string = "string"

// Define the range to conform to your needs
let range = string.startIndex..<string.index(string.startIndex, offsetBy: string.count / 2)
let substring = string[range]
// If the range is in the type "first x characters", like until the middle, you can use:
//  let substring = string.prefix(string.count / 2)

for (index, char) in zip(substring.indices, substring) {
    // index is the index in the substring
    print(char)
}

Note that using enumerated() will produce a pair of index and character, but the index is not the index of the character in the string. It is the index in the enumeration, which can be different.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.