The below code gives me an error. Is there a way to simply have this return nil?
var testArray: [Int]?
testArray = [1,2,3]
testArray?[9]
There are 3 possible things here that you might be conflating:
[Int]?[Int?]subscript(index) -> Int?.What you've got in your example is the first case – an optional of an array. That is, either testArray is nil, or it's a valid array containing zero or more elements.
To access anything inside an optional you have to first unwrap it. You can either force-unwrap it with ! (Try not to do this unless you have a really good reason – no not that reason. No not that one either). Or you can conditionally unwrap it, either using if let, or using one of the operators like ?, ?., ?? etc. In this case, testArray?[9] means, "if testArray is nil, then nil, otherwise {Some whatever is at position 9}. But there is no value at position 9 – this is only a 3-element array. So you get a runtime assertion.
(Another way of writing testArray?[9] would be testArray.map { $0[9] }, where inside the block the array is unwrapped and valid, but if it’s nil the block never gets executed. But the result is still that you try to access the 9th element of the array, and that's not allowed and you get a runtime error)
The second case, an array of optional integers, would mean that you could access testArray[1] and you'd get back {Some 2}, because the array holds optionals. But again, you can't access the 9th element, because there is no 9th element.
Finally the third case is where calling subscript gives you back an optional value, and if it's not a valid index, you get back a nil. This seems to be what you were expecting. Swift arrays do not do this. Swift dictionaries do, if you are looking up by key. If the key has a value, you get back {Some value}, if not, you get nil. The shorthand justification for this is that dictionaries contain sparse data, whereas arrays contain dense data.
If you're interested, I wrote posts about both the pro and anti argument for making arrays return optionals from subscript. But for now, like the other answers say, you have to check your bounds before accessing elements. You might want to try some of the helper methods like first, last, find, as well as map and filter etc rather than accessing elements directly, as this makes many of these problems go away.
(another interesting thing to think about – all 3 cases above can be combined together. So you could have an optional dictionary of optionals, that returned optionals when you accessed it. If you're still hazy on all this, try playing around with that in a playground :)
extension Array { func safeIndex(i: Int) -> Element? { return i < self.count && i >= 0 ? self[i] : nil } }Optional<Optional<Int>> (or Int?? for short). In cases where the value for a given key exists and is nil, a dictionary returns .Some(nil), which is very different to nil, which is what is returned if no value for that key exists. An optional-returning array subscript would work the same way.You need to do the range check explicitly, like this:
var testArray: [Int]?
testArray = [1,2,3]
let index1 = 9
let numberNine : Int? = index1 < testArray!.count ? testArray![ index1 ] : nil
println(numberNine)
let index2 = 2
let numberTwo : Int? = index2 < testArray!.count ? testArray![ index2 ] : nil
println(numberTwo!)
Conditional expression ... ? ... : ... will check the condition before deciding what side of the : to evaluate, so testArray![ index1 ] is protected by the condition: it will not cause an exception when the index is out of range.
testArray = [1,2,9] then things get messed up because it's based on the array count, and not the actual array key.testArray?[9] as an attempt to find value 9 in the array.If you want to know if the array contains a specific value you can use the global contains function:
var testArray: [Int]?
testArray = [1,2,3]
if let actualTestArray = testArray {
let doesContain9 = contains(actualTestArray, 9)
println(doesContain9) // prints "false"
}
If you can make testArray non-optional you can just do
var testArray: [Int]
testArray = [1,2,3]
let doesContain9 = contains(testArray, 9)
println(doesContain9) // prints "false"
When trying to access an index out of bound in Swift you should be thrown an error like EXC_BAD_INSTRUCTION at runtime.
Maybe you want to use an NSDictionary. In fact, with dictionary you will not have any runtime error when trying to access a key that doesn't exist. It will simply return nil
If you want to use array, you need to check the number of item you have in your array before accessing using the count property of your array