Start learning free

LESSON 115 OF 678 · ARRAYS I

Slicing an Array in Swift with Ranges and Array(...)

Lesson Read the full explanation

TL ; DR:

Array(arr[i..<j]) is a new array with the items from index i up to, not including, j. The original doesn't change.


let letters = ["a", "b", "c", "d", "e"]

let middle = Array(letters[1..<4])
print(middle)    // ["b", "c", "d"]

print(letters)   // ["a", "b", "c", "d", "e"]


Full lesson:

Sometimes you need only part of an array, like the first few items. A range of indexes picks that part.

1..<4 is the range from 1 up to, not including, 4. So letters[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, end the range at the count:

let tail = Array(letters[3..<letters.count])
print(tail)   // ["d", "e"]

On its own, letters[1..<4] is an ArraySlice, a view of the array's items. Wrap it in Array(...) to copy them into a new array.

Use the copy. An ArraySlice keeps the original indexes, so letters[1..<4][0] crashes. Its first index is 1, not 0.

The original array stays as it was.


Your task

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

Put the first three in a new array 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

    SWIFT · ARRAYS I

    Array Slicing in Swift, explained

    Taking part of an array

    Sometimes you need only part of an array, like the first few items. In Swift, you put a range of indexes in the subscript. The half-open range 1..<4 runs from 1 up to, but not including, 4, so letters[1..<4] holds the items at indexes 1, 2 and 3. The length is easy to see: 4 - 1 = 3 items.

    let letters = ["a", "b", "c", "d", "e"]
    
    let middle = Array(letters[1..<4])
    print(middle)
    print(letters)
    
    ["b", "c", "d"]
    ["a", "b", "c", "d", "e"]
    

    The original array is not changed. To go to the very end, end the range at the count, which is one past the last index:

    let tail = Array(letters[3..<letters.count])
    print(tail)
    
    ["d", "e"]
    

    Why Array(...)?

    On its own, letters[1..<4] is not an array but an ArraySlice, a view onto part of the original array's items. Wrapping it in Array(...) copies those items into a real array with its own indexes.

    The difference matters. A slice keeps the indexes of the array it came from, so its first index is 1, not 0:

    let view = letters[1..<4]
    print(view.startIndex)
    print(view[1])
    print(Array(view)[0])
    
    1
    b
    b
    

    That is why letters[1..<4][0] crashes the program: there is no index 0 in the slice. A function that returns [Int] or [String] also needs a real array, which is one more reason to wrap the slice.

    If you only need the first few or the last few items, there are shorter ways. prefix(3) and suffix(2) also give slices, so they get the same Array(...) wrapper:

    print(Array(letters.prefix(3)))
    print(Array(letters.suffix(2)))
    
    ["a", "b", "c"]
    ["d", "e"]
    

    Unlike a range, prefix does not crash when you ask for more items than the array has: it just gives everything. A range with an explicit end is stricter, which is good when a wrong number should be caught early. In this lesson, practise the ranges.

    The copy is independent

    Because Array(...) makes a new array, changing the copy leaves the original alone. Arrays in Swift are values, so this is the usual behaviour:

    var part = Array(letters[1..<4])
    part[0] = "X"
    print(part)
    print(letters)
    
    ["X", "c", "d"]
    ["a", "b", "c", "d", "e"]
    

    Note that part must be a var for this change to compile.

    Edges

    • An empty range such as 2..<2 is allowed and gives an empty array: Array(letters[2..<2]) is [].
    • A range that goes past the end, like 3..<9 on five items, stops the program with a run-time error. Keep the end at or below count.
    • The start must not be greater than the end.

    The exercise: a race podium

    runners holds ["Ana", "Ben", "Cleo", "Dan", "Eve"] in finishing order. The first three go in podium, which is the range 0..<3: indexes 0, 1 and 2.

    Everyone else goes in rest, from index 3 to the end. The range 3..<runners.count is 3..<5, so it holds indexes 3 and 4.

    let runners = ["Ana", "Ben", "Cleo", "Dan", "Eve"]
    let podium = Array(runners[0..<3])
    let rest = Array(runners[3..<runners.count])
    print(podium[0], podium[1], podium[2])
    print(rest[0], rest[1])
    print(runners.count)
    
    Ana Ben Cleo
    Dan Eve
    5
    

    The last line shows that runners still has all five names. The two new arrays are copies.

    Key takeaways

    • arr[i..<j] picks indexes i up to, not including, j.
    • That result is an ArraySlice. Wrap it as Array(arr[i..<j]) to get a new array that starts at index 0.
    • Use arr.count as the end to reach the last item.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free