What slicing is
Slicing means taking a run of consecutive characters out of a string. Swift offers two ways to do it: ranges of String.Index values, and the helper methods prefix, suffix, dropFirst and dropLast. Neither changes the original string.
Slicing with indices
A Swift string can't be subscripted with an integer like language[2]. Positions are String.Index values, and you build one by moving from startIndex by a number of characters. Two indices form a half-open range start..<end: the character at start is included and the one at end is not.
let language = "JavaScript"
let start = language.index(language.startIndex, offsetBy: 2)
let end = language.index(language.startIndex, offsetBy: 6)
let substring = String(language[start..<end])
print(substring)
print(language)
vaSc
JavaScript
The character at offset 2 (v) is in the result and the character at offset 6 (r) is not. The original language keeps its value.
To slice from an offset to the end, use an open-ended range, language[start...]. To slice from the beginning, use language[..<end].
let language = "JavaScript"
let start = language.index(language.startIndex, offsetBy: 4)
print(String(language[start...]))
Script
Substring and String
Subscripting a string with a range doesn't give a String. It gives a Substring, a view onto the original string's storage. That is cheap, because nothing is copied, but a Substring is a different type, so you can't pass it where a String is expected. Wrapping it in String(...) copies the characters into a real String that you can keep and use anywhere.
let language = "JavaScript"
let start = language.index(language.startIndex, offsetBy: 2)
let end = language.index(language.startIndex, offsetBy: 6)
print(type(of: language[start..<end]))
print(type(of: String(language[start..<end])))
Substring
String
Slicing with prefix, dropFirst and suffix
For most slices you don't need indices at all:
prefix(n)keeps the firstncharacters.suffix(n)keeps the lastncharacters.dropFirst(n)skips the firstncharacters and keeps the rest.dropLast(n)skips the lastncharacters and keeps the rest.
All four return a Substring. They can be chained: dropFirst(7).prefix(5) first skips 7 characters, then keeps the next 5. Unlike the index methods, they don't crash when n is too large. They just return as much as the string has:
let message = "Coding feels great"
print(message.prefix(100))
print(message.dropFirst(100).isEmpty)
Coding feels great
true
Moving an index past the end with index(_:offsetBy:) does crash the program, so use these helpers when the length isn't certain.
The exercise
The starter code has let message = "Coding feels great" and a final print that uses three constants you have to create. The characters are numbered from 0, so "Coding" takes positions 0 to 5, a space is at 6, "feels" takes 7 to 11, a space is at 12, and "great" takes 13 to 17.
firstWordholds the first 6 characters,prefix(6), converted withString(...).middleWordholds the characters at positions 7 to 11. Skip 7 withdropFirst(7), then keep 5 withprefix(5).lastWordholds everything from position 13 on, sodropFirst(13)is enough.suffix(5)would give the same result, counting from the end.
let message = "Coding feels great"
let firstWord = String(message.prefix(6))
let middleWord = String(message.dropFirst(7).prefix(5))
let lastWord = String(message.dropFirst(13))
print(firstWord + "\n" + middleWord + "\n" + lastWord)
Coding
feels
great
The conversion to String matters here: the + operator in the last line joins strings, so the pieces should be String values.
Common mistakes
- Counting the end index as included. In
start..<endthe end is excluded, so a slice from offset 2 to offset 6 has 4 characters. - Off-by-one with
dropFirst.dropFirst(7)removes 7 characters, so the next one is at position 7, the eighth character. Counting "8th through 12th" in human terms means starting at position 7. - Offsetting past the end.
index(_:offsetBy:)with a number larger than the string's length is a fatal error. - Keeping a
Substringaround. It shares the original string's storage, which stays alive for as long as theSubstringdoes. Convert it withString(...)when you want to store it.
Key takeaways
- Slice with
string[start..<end], where the indices come fromindex(_:offsetBy:), or useprefix,suffix,dropFirstanddropLast. - The end of a half-open range is excluded.
- Slices are
Substringvalues. Wrap them inString(...)to get aString. - Slicing never changes the original string.
