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 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 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
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
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
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
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
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
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.