ArraySlice with range operator and Prefix in Swift

The Swift standard library provides an ArraySlice to work with a subset of a larger collection without the overhead of making a copy of the original collection. Retrieving the first few elements of a collection can be achieved using the one-sided range operator, but is safer to use the prefix method without having to worry about an index out of range exception.

There are a number of variants of the prefix method on Swift collections, but the most useful in SwiftUI views is the one that takes the maxLength as a parameter. The advantage of using maxLength is that it does not have to be a valid index. Other forms of prefix and getting ArraySlices using the subscript syntax require that the indices specified are valid. The advantage of prefix(maxLength) is that it can be bound to a model whos elements change over time without the risk of a runtime error occurring.

It is interesting that the documentation of the other prefix methods recommends using the range subscript syntax and these can only be used with valid array indices or an "index out or range" error is raised.



Array Slice using range subscript

An ArraySlice is a view on an array. This can be very efficient as it does not require copying any elements in the array. There are warnings about long-term storage of ArraySlice instances as the ArraySlice holds a reference to the entire storage of the original array and may keep the larger array in memory longer than needed.

The half-open range operator (..<), creates ranges up to but not including the final value, whereas the closed range operator (...) includes the final value. It can be seen that the indices of the Array slice are the same as the original array. The indices of an ArraySlice, unlike an array, can have a nonzero start index as well as an end index less than the count.

1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
2
3nums[5..<9]
4// [6, 10, 14, 4]
5
6nums[5...9]
7// [6, 10, 14, 4, 11]

Array Slice using range subscript
Array Slice using range subscript



Array Slice using one-sided range

An ArraySlice can be obtained using a one-sided range to return a view on the beginning or the end of an array.

 1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
 2
 3
 4nums[..<7]
 5// [2, 7, 15, 8, 1, 6, 10]
 6
 7
 8nums[...7]
 9// [2, 7, 15, 8, 1, 6, 10, 14]
10
11
12nums[7...]
13// [14, 4, 11, 13, 12, 5, 3, 9]

Array Slice at begining or end
Array Slice at begining or end



Array Slice requires a valid index

An ArraySlice requires a valid index to be specified in the range syntax or a runtime error of "index out of range" will be raised.

1let nums = [2, 7, 15, 8, 1]
2
3
4nums[..<4]
5// [2, 7, 15, 8]
6
7
8nums[..<7]
9// 405: Fatal error: Array index is out of range

Array Slice requires a valid index
Array Slice requires a valid index



Prefix

Array provides an instance method prefix to return a view on the initial elements upto a specified maximum length. The maxLength parameter has to be a number greater than or equal to zero, but it does not have to be a valid index in the array. If the maxLength is greater than the number of elements in the array, then the entire array is returned.

1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
2
3
4nums.prefix[7]
5// [2, 7, 15, 8, 1, 6]
6
7
8nums[..<7]
9// [2, 7, 15, 8, 1, 6]

Array Slice from Prefix method
Array Slice from Prefix method



Prefix - maxLength does NOT have to be a valid index

Prefix works the same as a one-sided ArraySlice, except Prefix does not throw an error if the maxLength is outside the number of elements in the array, whereas the ArraySlice will.

1let nums = [2, 7, 15, 8, 1]
2
3
4nums.prefix[7]
5// [2, 7, 15, 8, 1]
6
7
8nums[..<7]
9// 405: Fatal error: Array index is out of range

maxLength in Prefix method does not have to be a valid index
maxLength in Prefix method does not have to be a valid index



Prefix(through:)

There is a version of Prefix is prefix(through:), that returns a view on the original array from the start of the array through the specified position. The documentation on this method states that it is preferred to use the subscript notation and there is no advantage as Prefix(through:) throws an error if the value specified is not a valid index in the array.

Using the prefix(through:) method is equivalent to using a partial closed range as the collection’s subscript. The subscript notation is preferred over prefix(through:).

developer.apple.com/documentation

1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
2
3nums.prefix(through: 7)
4// [2, 7, 15, 8, 1, 6, 10, 14]
5
6nums[...7]
7// [2, 7, 15, 8, 1, 6, 10, 14]

ArraySlice from prefix(through:) method
ArraySlice from prefix(through:) method



prefix(upTo:)

Another version of Prefix is prefix(upTo:), that returns a view on the original array from the start of the array up to, but not including the specified position. The documentation on this method also states that it is preferred to use the subscript notation.

1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
2
3nums.prefix(upTo: 7)
4// [2, 7, 15, 8, 1, 6, 10]
5
6nums[..<7]
7// [2, 7, 15, 8, 1, 6, 10]

ArraySlice from prefix(upto:) method
ArraySlice from prefix(upto:) method



prefix(while:)

The final Prefix method is prefix(while:), which returns a view on the original array from the initial element up until the predicate is false. The predicate is a logical condition on each element of the array in the form of a closure that returns true if the element matches the logical condition and false if it does not. prefix(while:) stops iterating over the array when the predicate returns false.

1let nums = [2, 7, 15, 8, 1, 6, 10, 14, 4, 11, 13, 12, 5, 3, 9]
2
3nums.prefix(while: {$0 != 6}) 
4// [2, 7, 15, 8, 1]
5
6nums.prefix(while: {$0 < 10})
7// [2, 7]

ArraySlice from prefix(while:) method
ArraySlice from prefix(while:) method




Conclusion

ArraySlice is used in Swift to return a view on a subset of a collection without the overhead of making a copy of the collection. The half-open range (..<) and closed range (...) operators are used to specify the index range to retrieve. An error will be thrown if the indices specified are out of range. The indices of the ArraySlice are the same as the original collection as this is a view on the collection.

The prefix method on Swift collections returns an ArraySlice from the beginning of a collection. Prefix has the advantage of specifying a maximum length to retrieve, but the maxLength can exceed the size of the collection without throwing an error. Prefix can be used in ViewModels or Views in SwiftUI to return the first number of elements in a collection even when the underlying data elements in the collection change, without the risk of a runtime error.

The code for ArraySliceApp used to visualise the slicing is available on GitHub.