Copying part of a buffer
Sometimes you need only part of an ArrayBuffer, such as the first few items. slice(i, j) copies the items from index i up to, but not including, index j into a new ArrayBuffer.
val letters = scala.collection.mutable.ArrayBuffer("a", "b", "c", "d", "e")
val middle = letters.slice(1, 4)
println(middle) // ArrayBuffer(b, c, d)
println(letters) // ArrayBuffer(a, b, c, d, e)
The item at the end index is left out, so letters.slice(1, 4) has the items at indexes 1, 2 and 3. The length is easy to see: 4 - 1 = 3 items. The original buffer stays as it was.
Going to the end
To slice up to the very end, use the size as the end index. The size is one past the last index, so nothing is left out:
val tail = letters.slice(3, letters.size)
println(tail) // ArrayBuffer(d, e)
An end index past the size is not an error. letters.slice(3, 100) stops at the end and gives the same ArrayBuffer(d, e).
Edge cases
- Empty results.
slice(2, 2)andslice(3, 1)both give an empty buffer,ArrayBuffer(). Slicing doesn't throw when the range holds nothing. - A start below zero counts as 0:
letters.slice(-2, 2)isArrayBuffer(a, b). Don't rely on it; write the index you mean. - It is a copy. Changing an item of the slice doesn't change the original. After
val c = letters.slice(0, 5)andc(0) = "Z",cstarts withZandlettersstill starts witha.
Scala also has take(n) for the first n items and drop(n) for everything after them. Here letters.take(2) is ArrayBuffer(a, b) and letters.drop(3) is ArrayBuffer(d, e). This lesson practises slice, which handles any range.
Reading the two numbers
It helps to think of the indexes as marks placed between the items. In a b c d e, mark 0 sits before a, mark 1 between a and b, and mark 5 after e. A slice copies what lies between two marks. So slice(1, 4) takes everything between mark 1 and mark 4, which is b c d, and slice(0, 3) takes the first three items. This also explains why the size works as an end: it is the last mark, after the final item.
Common mistakes
The usual slip is expecting the end index to be included, so slice(0, 3) is read as four items. Count 3 - 0 instead. Another is ending at size - 1 to reach the last item, which leaves the last item out; end at size. Last, remember that the slice is a new buffer: code that changes the slice doesn't touch the original.
The task
runners holds a race's runners in finishing order. The first three go in a new buffer named podium, and everyone from index 3 to the end goes in one named rest. The code then prints both and the length of runners.
podiumisrunners.slice(0, 3). Index 3 is not included, so you get the items at 0, 1 and 2.restisrunners.slice(3, runners.size), which runs to the end.
val runners = scala.collection.mutable.ArrayBuffer("Ana", "Ben", "Cleo", "Dan", "Eve")
val podium = runners.slice(0, 3)
val rest = runners.slice(3, runners.size)
println(podium(0) + " " + podium(1) + " " + podium(2))
println(rest(0) + " " + rest(1))
println(runners.size)
It prints:
Ana Ben Cleo
Dan Eve
5
The last line shows that slicing didn't remove anyone from runners.
Key takeaways
slice(i, j)gives a new buffer with the items at indexesitoj - 1.- The end index is excluded, and
j - iis the length when the range fits. - Use
sizeas the end to reach the last item; a too-large end is safe. - The original buffer never changes.
