Cutting a piece out of a string
Besides one character, you can take a run of consecutive characters, called a substring. Scala strings are Java strings, so substring is available:
string.substring(startIndex, endIndex)
startIndex is the zero-based index of the first character you keep. endIndex is the index where the cut stops, and that character is not included. So the result has endIndex - startIndex characters.
val language = "Scala"
val part = language.substring(1, 4)
println(part) // cal
println(language) // Scala
cal
Scala
Indexes 1, 2 and 3 are kept (c, a, l), and index 4 is excluded. The original string is untouched: slicing builds a new string.
Leaving out the end
With one argument, substring runs to the end of the string:
val language = "Scala"
println(language.substring(3)) // la
The exercise, step by step
The lesson gives you this string. Counting indexes helps: Coding is indexes 0 to 5, a space is at 6, feels is 7 to 11, a space is at 12, and great is 13 to 17.
val message = "Coding feels great"
firstWord: the first 6 characters. They are indexes 0 to 5. The end index is excluded, so the call issubstring(0, 6).middleWord: the 8th to 12th characters. Counting from 1, those are indexes 7 to 11. The end is excluded, so the call issubstring(7, 12).lastWord: the last 5 characters. The string has 18 characters, so they start at index 13. Run to the end withsubstring(13).
val message = "Coding feels great"
val firstWord = message.substring(0, 6)
val middleWord = message.substring(7, 12)
val lastWord = message.substring(13)
These values are Coding, feels and great.
Pitfalls
- Bad bounds throw.
"abc".substring(0, 10)throws aStringIndexOutOfBoundsExceptionwith the messageRange [0, 10) out of bounds for length 3. The same happens when the start is larger than the end:"abc".substring(2, 1)fails withRange [2, 1) out of bounds for length 3. - The edge is allowed.
"abc".substring(3)returns the empty string, because a start equal to the length is valid.substring(4)throws. - Length of the result. It is end minus start. A slice of 5 characters starting at 7 ends at 12, not 11.
A forgiving alternative: slice
Scala's own slice method takes the same two arguments but clamps them instead of throwing:
println("abc".slice(1, 10)) // bc
println("abc".slice(2, 1)) // prints an empty line
println("abc".take(10)) // abc
println("abc".drop(1)) // bc
take(n) keeps the first n characters and drop(n) removes them. Use substring when an out-of-range index would be a bug you want to hear about, and slice, take or drop when short strings are normal.
Slicing with indexOf
Hard-coded numbers only work for one string. Combining substring with indexOf finds the cut point for you:
val sentence = "Hello World"
val space = sentence.indexOf(" ")
println(sentence.substring(0, space)) // Hello
println(sentence.substring(space + 1)) // World
The first slice stops at the space, which is excluded. The second starts one past the space. The same pattern works for the last n characters: s.substring(s.length - n). To drop the final character, use s.substring(0, s.length - 1), or s.dropRight(1).
Reading a slice in your head
A reliable habit is to write the index under each character before choosing the numbers. For "Coding feels great" the word feels starts at the f (index 7) and the next character after it is the space at index 12. Start at the first character you want; end at the first character you don't. This "half-open" convention also explains why s.substring(0, k) always has exactly k characters, and why s.substring(0, k) followed by s.substring(k) rebuilds the whole string.
Key takeaways
s.substring(a, b)keeps indexesaup tob - 1.s.substring(a)keeps everything fromato the end.- The original string never changes.
- Out-of-range bounds throw with
substringbut are clamped byslice.
