Start learning free

LESSON 115 OF 677 · ARRAYS I

Slicing an ArrayBuffer in Scala

Lesson Read the full explanation

TL ; DR:

buf.slice(i, j) is a new ArrayBuffer with the items from index i up to, not including, j. The original doesn't change.


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)


Full lesson:

Sometimes you need only part of an ArrayBuffer, like the first few items. The slice method copies that part into a new ArrayBuffer.

Give it the start index and the end index. The item at the end index is not included. So letters.slice(1, 4) has the items at 1, 2 and 3.

That makes the length easy to see: 4 - 1 = 3 items.

To go to the very end, use the size as the end index:

val tail = letters.slice(3, letters.size)
println(tail)   // ArrayBuffer(d, e)

The original ArrayBuffer stays as it was.

An end past the size is no error. So letters.slice(3, 100) simply stops at the end.


Your task

runners holds a race's runners, in the order they finished.

Put the first three in a new ArrayBuffer named podium. Put everyone else, from index 3 to the end, in one named rest.

The code below prints both, then the length of runners.

Expected output:

Ana Ben Cleo
Dan Eve
5
STEP 1 OF 2
    Ctrl + Enter to check

    SCALA · ARRAYS I

    Array Slicing in Scala, explained

    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) and slice(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) is ArrayBuffer(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) and c(0) = "Z", c starts with Z and letters still starts with a.

    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.

    1. podium is runners.slice(0, 3). Index 3 is not included, so you get the items at 0, 1 and 2.
    2. rest is runners.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 indexes i to j - 1.
    • The end index is excluded, and j - i is the length when the range fits.
    • Use size as the end to reach the last item; a too-large end is safe.
    • The original buffer never changes.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free