I'm trying to make a safe way of splitting an array.
I know you can take a subset of an array doing something like this:
let arr = [1,2,3,4,5]
print(arr[0..<3])
It will print [1,2,3]
If you try the following on that same arr
print(arr[3..<9])
The program will crash
I want to make an array extension that will instead of crashing, yield as many of the elements as it can so it would print
[4,5]
subscript(safe range: Range) -> Element? {
}
Overview. The ArraySlice type makes it fast and efficient for you to perform operations on sections of a larger array. Instead of copying over the elements of a slice to new storage, an ArraySlice instance presents a view onto the storage of a larger array.
You don't have to reduce an array to get it into a set; just create the set with an array: let objectSet = Set(objects.
Array is faster than set in terms of initialization. Set is slower than an array in terms of initialization because it uses a hash process. The array allows to store duplicate elements in it. Set doesn't allow to store duplicate elements in it.
You could do the following:
extension Array {
subscript(safe range: Range<Index>) -> ArraySlice<Element>? {
if range.endIndex > endIndex {
if range.startIndex >= endIndex {return nil}
else {return self[range.startIndex..<endIndex]}
}
else {
return self[range]
}
}
}
let a = [1,2,3]
a[safe: 1...3] // [2,3]
Edit: given the comment that the start index might not be the beginning of the array, I've amended so that the returned slice will always begin at startIndex even if the endIndex goes beyond bounds of the Array (unless start index is after endIndex of Array in which case nil is returned).
EDIT: Updated to a more straight forward version.
Just made for the practice. Same safe
naming as the others used for clarity; note that it does not return nil but an empty array for out of bounds indexing, that avoids null checks in the consuming code for many cases.
extension Array {
subscript(safe range: Range<Index>) -> ArraySlice<Element> {
return self[min(range.startIndex, self.endIndex)..<min(range.endIndex, self.endIndex)]
}
}
let a = [1,2,3]
a[safe: 1..<17] // [2,3]
a[safe: 4..<17] // []
a[safe: 1..<2] // [2]
...or an alternate - more straight forward - version;
You can use the
extension RandomAccessIndexType {
@warn_unused_result
public func advancedBy(n: Self.Distance, limit: Self) -> Self
}
method to limit the given range to the valid range of the given array:
extension Array {
public subscript (safe subRange: Range<Int>) -> ArraySlice<Element> {
let from = startIndex.advancedBy(subRange.startIndex, limit: endIndex)
let to = startIndex.advancedBy(subRange.endIndex, limit: endIndex)
return self[from ..< to]
}
}
let arr = [1,2,3,4,5]
print(arr[safe: 3..<10]) // [4, 5]
print(arr[safe: 9..<10]) // []
Update for Swift 3: Indexing collections has changed a lot.
Now you can use the index(...)
methods defined in the
BidirectionalIndexable
protocol:
extension Array {
public subscript (safe subRange: Range<Int>) -> ArraySlice<Element> {
let from = index(startIndex, offsetBy: subRange.lowerBound, limitedBy: endIndex) ?? endIndex
let to = index(startIndex, offsetBy: subRange.upperBound, limitedBy: endIndex) ?? endIndex
return self[from ..< to]
}
}
You should prefer an empty collection over an optional collection. This works well with swift 4.
extension Array {
/*
Safe array access via range, accounting for array bounds
examples:
[1, 2, 3][0...6] -> []
[1, 2, 3][0...1] -> [1, 2]
*/
subscript(safe range: Range<Index>) -> [Element] {
guard
range.startIndex >= self.startIndex,
range.endIndex <= self.endIndex
else {
return []
}
return Array(self[range])
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With