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..<2is allowed and gives an empty array:Array(letters[2..<2])is[]. - A range that goes past the end, like
3..<9on five items, stops the program with a run-time error. Keep the end at or belowcount. - 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 indexesiup to, not including,j.- That result is an
ArraySlice. Wrap it asArray(arr[i..<j])to get a new array that starts at index 0. - Use
arr.countas the end to reach the last item.
