Reputation: 55705
Given an array that contains any number of objects, how could you cleanly and safely get the first 3 elements out of it to store in a new array? If the array does not contain at least 3 elements, it should not trigger a runtime exception, instead it should only add the number of elements in the array to the new array.
I thought this might work, but it won't compile in Xcode 7, and if it did I don't imagine it would behave safely as I desire:
let arr1 = [1, 2, 3, 4, 5]
let arr2 = arr1[0..<3]
//Expected: arr == [1, 2, 3]
let arr1 = [1, 2]
let arr2 = arr1[0..<3]
//Expected: arr2 == [1, 2]
let arr1 = [Int]()
let arr2 = arr1[0..<3]
//Expected: arr2 == []
Of course one could always do something like this, or you could use a for loop, but neither is clean and concise. I want a to find a swiftier way.
let arr1 = [1, 2]
var arr2 = [Int]()
if photos.count > 0 {
arr2.append(arr1[0])
}
if photos.count > 1 {
arr2.append(arr1[1])
}
if photos.count > 2 {
arr2.append(arr1[2])
}
Upvotes: 0
Views: 221
Reputation: 66242
You can implement what you want with an extension to Array
:
extension Array {
func safeRange(range : Range<Int>) -> ArraySlice<Element> {
guard range.startIndex >= 0 && range.endIndex >= 0 else {
fatalError("Ranges with negative numbers aren't supported")
}
var shrinkingRange = range
while shrinkingRange.endIndex > shrinkingRange.startIndex + 1 {
if shrinkingRange.endIndex <= self.count {
return self[shrinkingRange]
}
shrinkingRange.endIndex = shrinkingRange.endIndex - 1
}
return []
}
}
The examples you gave behave as expected:
let arr1 = [1, 2, 3, 4, 5]
let arr2 = arr1.safeRange(0..<3)
//Expected: arr == [1, 2, 3]
let arr3 = [1, 2]
let arr4 = arr3.safeRange(0..<3)
//Expected: arr2 == [1, 2]
let arr5 = [Int]()
let arr6 = arr5.safeRange(0..<3)
//Expected: arr2 == []
You could also use filter
:
extension CollectionType where Generator.Element : Equatable, Index == Int {
func safeRange(range : Range<Int>) -> [Self.Generator.Element] {
return self.filter {
let index = self.indexOf($0)!
return index >= range.startIndex && index < range.endIndex
}
}
}
(This may fail if your array contains duplicate elements, since indexOf
returns the index of the first instance.)
Upvotes: 0
Reputation: 27236
Another approach is to use a function…
import Swift
let arr1 = [1, 2, 3, 4, 5]
let arr2 = [1, 2]
let arr3 = [Int]()
func firstThree(data: [Int]) -> [Int] {
var results = [Int]()
for (index, number) in data.enumerate() {
if (index < 3) {
results.append(number)
}
}
return results
}
print(firstThree(arr1))
print(firstThree(arr2))
print(firstThree(arr3))
This prints:
[1, 2, 3]
[1, 2]
[]
A slightly better approach would be to use generics and get an N number of items:
func genericFirstItems<T>(array: [T], numberOfItems: Int) -> [T] {
var results = [T]()
for (index, item) in array.enumerate() {
if index < numberOfItems {
results.append(item)
}
}
return results
}
print(genericFirstItems(arr1, numberOfItems: 3))
print(genericFirstItems(arr2, numberOfItems: 3))
print(genericFirstItems(arr3, numberOfItems: 3))
This has the same output.
[1, 2, 3]
[1, 2]
[]
Upvotes: 0