350

If I have an array in Swift, and try to access an index that is out of bounds, there is an unsurprising runtime error:

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

However, I would have thought with all the optional chaining and safety that Swift brings, it would be trivial to do something like:

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

Instead of:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

But this is not the case - I have to use the ol' if statement to check and ensure the index is less than str.count.

I tried adding my own subscript() implementation, but I'm not sure how to pass the call to the original implementation, or to access the items (index-based) without using subscript notation:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}
Craig Otis
  • 31,257
  • 32
  • 136
  • 234
  • 2
    I realize this is slightly OT, but I also feel it would be nice if Swift had clear syntax for performing any sort of bounds check, including lists. We already have a suitable keyword for this, in. So for instance, if X in (1,2,7)... or if X in myArray – Maury Markowitz Oct 24 '16 at 13:49
  • 4
    Be aware that most solutions below 1) use `indices.contains` which is O(n) –thus, terribly inefficient for arrays, and 2) disregard the case where you are actually storing nil objects. – Jano Aug 31 '20 at 09:55
  • I ran some comparisons on an array with 10 million complex objects using @nikita's `indices.contains` (O(n)) and @zubko's bound checking with `return index >= startIndex && index < endIndex` (O(1)). On an iPhone 11, the O(1) solution ran 15x faster than the O(n) solution but they both completed within 1/10 of a millisecond. So yes, the accepted answer is less efficient but the difference is not noticeable. If it's a major concern, I recommend adding an `Array` extension with the same method signature that uses bound checking, and keeping the `Collection` extension that uses `contains`. – Nathan Dudley Jul 07 '21 at 04:46

22 Answers22

835

Alex's answer has good advice and solution for the question, however, I've happened to stumble on a nicer way of implementing this functionality:

extension Collection {
    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Example

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}
Nikita Kukushkin
  • 14,648
  • 4
  • 37
  • 45
  • 62
    I think this definitely deserves attention - nice work. I like the included `safe:` parameter name to ensure the difference. – Craig Otis Jun 02 '15 at 11:25
  • 12
    As of Swift 2 (Xcode 7) this needs a little tweak: `return self.indices ~= index ? self[index] : nil;` – Tim Jun 15 '15 at 05:34
  • Swift 2 (Xcode 7) also needs to replace `-> T?` with `-> Element?` – Justin Levi Winter Jul 19 '15 at 17:31
  • In your Swift 2.0 example, I believe that `return count <= index ? nil : self[index]` would also work in place of `return indices ~= index ? self[index] : nil`. Is there a reason to prefer one over the other? The first is more readable to me, since I'm unfamiliar with `~=` and CollectionType's property `indices`. – John Sauer Nov 15 '15 at 18:23
  • 3
    The reason is subjective. I read my implementation as "array's indices include index". It's concise, and, to me, seems simpler than a bounds check made of comparisons. I also like to play with new API. You can freely replace my implementation with yours, however you may also want to add a check for negative indexes to be completely `safe`. – Nikita Kukushkin Nov 15 '15 at 20:24
  • 5
    Hey, I've updated the answer for Swift 3. I'll be stuck with Swift 2 for a while longer, so if anything breaks, feel free to point it out. – Nikita Kukushkin Jul 18 '16 at 18:54
  • To merge all code, you can use #if swift(>=3.0) #else #endif to have all in one file and be ready for your futur Swift upgrade ;) – Dam Sep 05 '16 at 10:32
  • 11
    Regarding the Swift 3 version: possibly a corner-case-only-prompt, but a prompt nonetheless: there are cases where the "safe" subscript version above isn't safe (whereas the Swift 2 version was): for `Collection` types where the `Indices` are not contiguous. E.g. for `Set` instances, if we were to access a set element by index (`SetIndex`), we can run into runtime exceptions for indices that are `>= startIndex` and `< endIndex`, in which case the safe subscript fails (see e.g. [this contrived example](https://gist.github.com/dfrib/85398f9d3d5bfc9757905b499d79e26f)). – dfrib Oct 30 '16 at 18:57
  • @dfri Good point. I think this would work as a more complete Swift 3 version: https://gist.github.com/JJC1138/501ef8186cab49a8f780c2345cff94e7 – Jon Colverson Nov 14 '16 at 17:04
  • 2
    @JonColverson yes, that should be safe even for corner cases, noting however that we loose the `O(1)` random index access (which we did, however, also for the `contains` Swift 2 version above), which shouldn't be an issue, however, unless working in some HPC application with huge arrays. For another alternative (similarily `O(n)`), which would correspond to a more direct Swift 2->3 translation of the Swift 2 version above, see [the answer in this Q&A](http://stackoverflow.com/a/40331858/4573247) (use of `contains` also allows "short-circuiting", as in your explicit `while` loop solution). – dfrib Nov 14 '16 at 17:12
  • 2
    Can someone explain the type constraints for the Swift 3 implementation, and how they were determined? Without them, Swift tries to use the wrong implementation of `contains()`; it tries to use `contains(where: _)`. I'm digging through the reference docs to follow along with the (inherited) associated types, and I'm not sure why the provided constraint solves the problem. How did you derive it? – Ian Dec 08 '16 at 14:29
  • Shouldn't `safe` be called `unsafe` instead ? I understand that the subscript method itself is safe, but since we're naming the parameter, which is potentially unsafe, it seems odd. – Joan Jan 13 '17 at 12:58
  • I don't know if it is also your case, but when I add this, I can't connect the delegate of a tableview in interface builder (the option disappear oddly). I don't know if it is linked. – darksider Mar 22 '17 at 09:31
  • Is there a way to override the existent subscript? i.e., call it without the `safe:` – Rodrigo Ruiz Sep 25 '17 at 22:03
  • In my case, Swift4 still crash with "Index out of range" – Dekel Maman Oct 31 '17 at 11:44
  • 14
    To prevent generating indices and iterating over them (O(n)), it's better to use comparisons (O(1)): `return index >= startIndex && index < endIndex ? self[index] : nil` `Collection` types have `startIndex`, `endIndex` which are `Comparable` . Of course, this won't work for some strange collections which, for example, don't have indices in the middle, solution with `indices` is more general one. – zubko Jun 20 '19 at 21:22
  • Strictly speaking, `safe` is not really "safe" in terms of multi-threading, right? It's still possible that `indices.contains(index)` will return `true` but the array will be modified before `self[index]` is called. Maybe it makes sense either to prefer another name for the subscript or add a comment/documentation so the users of the method don't forget about this case. – Legonaftik May 10 '20 at 10:18
  • @Legonaftik All collections in the Swift Standard Library are value types, so the subscript is operating on a copy of the collection, no changes from another thread can happen between the `indices.contains(index)` and the `self[index]` – Snowy_1803 Jul 05 '20 at 13:27
  • How to get the Line# of the offending statement? – San Lewy Oct 27 '22 at 21:18
59

If you really want this behavior, it smells like you want a Dictionary instead of an Array. Dictionaries return nil when accessing missing keys, which makes sense because it's much harder to know if a key is present in a dictionary since those keys can be anything, where in an array the key must in a range of: 0 to count. And it's incredibly common to iterate over this range, where you can be absolutely sure have a real value on each iteration of a loop.

I think the reason it doesn't work this way is a design choice made by the Swift developers. Take your example:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"

If you already know the index exists, as you do in most cases where you use an array, this code is great. However, if accessing a subscript could possibly return nil then you have changed the return type of Array's subscript method to be an optional. This changes your code to:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
//                                     ^ Added

Which means you would need to unwrap an optional every time you iterated through an array, or did anything else with a known index, just because rarely you might access an out of bounds index. The Swift designers opted for less unwrapping of optionals, at the expense of a runtime exception when accessing out of bounds indexes. And a crash is preferable to a logic error caused by a nil you didn't expect in your data somewhere.

And I agree with them. So you won't be changing the default Array implementation because you would break all the code that expects a non-optional values from arrays.

Instead, you could subclass Array, and override subscript to return an optional. Or, more practically, you could extend Array with a non-subscript method that does this.

extension Array {

    // Safely lookup an index that might be out of bounds,
    // returning nil if it does not exist
    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }
}

var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana
}

if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil
}

Swift 3 Update

func get(index: Int) ->T? needs to be replaced by func get(index: Int) ->Element?

Chris Frederick
  • 5,482
  • 3
  • 36
  • 44
Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • 4
    +1 (and the accept) for mentioning the issue with changing the return type of `subscript()` to an optional - this was the primary roadblock faced in overriding the default behavior. (I couldn't actually get it to work *at all.*) I was avoiding writing a `get()` extension method, which is the obvious choice in other scenarios (Obj-C categories, anyone?) but `get(` isn't much bigger than `[`, and makes it clear that the behavior may differ from what other developers may expect out of the Swift subscript operator. Thank you! – Craig Otis Aug 15 '14 at 17:27
  • 5
    To make it even shorter, I use at() ;) Thanks! – hyouuu Jun 02 '15 at 07:45
  • 8
    As of Swift 2.0 `T` has been renamed to `Element`. Just a friendly reminder :) – Stas Zhukovskiy Dec 07 '15 at 10:56
  • 4
    To add onto this discussion, another reason why bounds checking isn't baked into Swift to return an optional is because returning `nil` instead of causing an exception from an out-of-bounds index would be ambiguous. Since e.g. `Array` could also return nil as a valid member of the collection, you wouldn't be able to differentiate between those two cases. If you have your own collection type that you know can never return a `nil` value, aka it's contextual to the application, then you could extend Swift for safe bounds checking as answered in this post. – Aaron Jun 13 '17 at 21:30
  • Works beautifully – KamyFC Dec 05 '19 at 08:03
43

To build on Nikita Kukushkin's answer, sometimes you need to safely assign to array indexes as well as read from them, i.e.

myArray[safe: badIndex] = newValue

So here is an update to Nikita's answer (Swift 3.2) that also allows safely writing to mutable array indexes, by adding the safe: parameter name.

extension Collection {
    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

extension MutableCollection {
    subscript(safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }

        set(newValue) {
            if let newValue = newValue, indices.contains(index) {
                self[index] = newValue
            }
        }
    }
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
SafeFastExpressive
  • 3,637
  • 2
  • 32
  • 39
  • 7
    Extremely underrated answer! This is the correct way tot do this! – Reid Oct 20 '18 at 04:59
  • 2
    I copied this answer a long time ago and I came back because of 'MutableCollection' being deleted in my project. I can't give you more likes I am afraid! – Reimond Hill Nov 23 '20 at 12:24
27
extension Array {
    subscript (safe index: Index) -> Element? {
        0 <= index && index < count ? self[index] : nil
    }
}
  • O(1) performance
  • type safe
  • correctly deals with Optionals for [MyType?] (returns MyType??, that can be unwrapped on both levels)
  • does not lead to problems for Sets
  • concise code

Here are some tests I ran for you:

let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int? (`b` contains a value and that value is `nil`)
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?
CommaToast
  • 11,370
  • 7
  • 54
  • 69
thetrutz
  • 1,395
  • 13
  • 12
  • I think it's up for debate as to whether this is the "correct" way to deal with optionals. I can see `b ?? 5` returning `nil` leading to more bugs than if it returned 5. – Carson Holzheimer Aug 31 '20 at 12:45
  • 1
    I enhanced the comment in my answer, to clarify the situation: b contains a value and the value is nil. If we casted automatically to `Int?`, we would loose the information, if we got a hit or a miss in the array, which might be necessary in some circumstances. I would rather debate, if Swift could get rid of automatically casting values to optionals (e.g. in comparisons). This is dangerous, because it is silent. If you look for example at Haskell's `Maybe` type and then look at Swift, you would get the feeling, Swift is broken with regards to optionals and its special (non-necessary) syntax. – thetrutz Sep 01 '20 at 13:47
  • count is O(N) performance – Carl Hung Jul 11 '23 at 19:21
22

Swift 4

An extension for those who prefer a more traditional syntax:

extension Array {
    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Matjan
  • 3,591
  • 1
  • 33
  • 31
14

Valid in Swift 2

Even though this has been answered plenty of times already, I'd like to present an answer more in line in where the fashion of Swift programming is going, which in Crusty's words¹ is: "Think protocols first"

• What do we want to do?
- Get an Element of an Array given an Index only when it's safe, and nil otherwise
• What should this functionality base it's implementation on?
- Array subscripting
• Where does it get this feature from?
- Its definition of struct Array in the Swift module has it
• Nothing more generic/abstract?
- It adopts protocol CollectionType which ensures it as well
• Nothing more generic/abstract?
- It adopts protocol Indexable as well...
• Yup, sounds like the best we can do. Can we then extend it to have this feature we want?
- But we have very limited types (no Int) and properties (no count) to work with now!
• It will be enough. Swift's stdlib is done pretty well ;)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

¹: not true, but it gives the idea

DeFrenZ
  • 2,172
  • 1
  • 20
  • 19
  • 2
    As a Swift newbie I don't understand this answer. What does the code at the end represent? Is that a solution, and if so, how do I actually use it? – Thomas Tempelmann May 17 '17 at 17:28
  • 3
    Sorry, this answer isn't valid anymore for Swift 3, but the process certainly is. The only difference is that now you should stop at `Collection` probably :) – DeFrenZ May 17 '17 at 18:30
12
  • Because arrays may store nil values, it does not make sense to return a nil if an array[index] call is out of bounds.
  • Because we do not know how a user would like to handle out of bounds problems, it does not make sense to use custom operators.
  • In contrast, use traditional control flow for unwrapping objects and ensure type safety.

if let index = array.checkIndexForSafety(index:Int)

  let item = array[safeIndex: index] 

if let index = array.checkIndexForSafety(index:Int)

  array[safeIndex: safeIndex] = myObject
extension Array {

    @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {

        if indices.contains(index) {

            // wrap index number in object, so can ensure type safety
            return SafeIndex(indexNumber: index)

        } else {
            return nil
        }
    }

    subscript(index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

    // second version of same subscript, but with different method signature, allowing user to highlight using safe index
    subscript(safeIndex index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

}

public class SafeIndex {

    var indexNumber:Int

    init(indexNumber:Int){
        self.indexNumber = indexNumber
    }
}
M.S. Cline
  • 121
  • 1
  • 4
11

I realize this is an old question. I'm using Swift5.1 at this point, the OP was for Swift 1 or 2?

I needed something like this today, but I didn't want to add a full scale extension for just the one place and wanted something more functional (more thread safe?). I also didn't need to protect against negative indices, just those that might be past the end of an array:

let fruit = ["Apple", "Banana", "Coconut"]

let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil

For those arguing about Sequences with nil's, what do you do about the first and last properties that return nil for empty collections?

I liked this because I could just grab at existing stuff and use it to get the result I wanted. I also know that dropFirst(n) is not a whole collection copy, just a slice. And then the already existent behavior of first takes over for me.

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
8

Swift 5.x

An extension on RandomAccessCollection means that this can also work for ArraySlice from a single implementation. We use startIndex and endIndex as array slices use the indexes from the underlying parent Array.

public extension RandomAccessCollection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    /// - complexity: O(1)
    subscript (safe index: Index) -> Element? {
        guard index >= startIndex, index < endIndex else {
            return nil
        }
        return self[index]
    }
    
}
Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
6

I found safe array get, set, insert, remove very useful. I prefer to log and ignore the errors as all else soon gets hard to manage. Full code bellow

/**
 Safe array get, set, insert and delete.
 All action that would cause an error are ignored.
 */
extension Array {

    /**
     Removes element at index.
     Action that would cause an error are ignored.
     */
    mutating func remove(safeAt index: Index) {
        guard index >= 0 && index < count else {
            print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
            return
        }

        remove(at: index)
    }

    /**
     Inserts element at index.
     Action that would cause an error are ignored.
     */
    mutating func insert(_ element: Element, safeAt index: Index) {
        guard index >= 0 && index <= count else {
            print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
            return
        }

        insert(element, at: index)
    }

    /**
     Safe get set subscript.
     Action that would cause an error are ignored.
     */
    subscript (safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }
        set {
            remove(safeAt: index)

            if let element = newValue {
                insert(element, safeAt: index)
            }
        }
    }
}

Tests

import XCTest

class SafeArrayTest: XCTestCase {
    func testRemove_Successful() {
        var array = [1, 2, 3]

        array.remove(safeAt: 1)

        XCTAssert(array == [1, 3])
    }

    func testRemove_Failure() {
        var array = [1, 2, 3]

        array.remove(safeAt: 3)

        XCTAssert(array == [1, 2, 3])
    }

    func testInsert_Successful() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 1)

        XCTAssert(array == [1, 4, 2, 3])
    }

    func testInsert_Successful_AtEnd() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 3)

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testInsert_Failure() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 5)

        XCTAssert(array == [1, 2, 3])
    }

    func testGet_Successful() {
        var array = [1, 2, 3]

        let element = array[safe: 1]

        XCTAssert(element == 2)
    }

    func testGet_Failure() {
        var array = [1, 2, 3]

        let element = array[safe: 4]

        XCTAssert(element == nil)
    }

    func testSet_Successful() {
        var array = [1, 2, 3]

        array[safe: 1] = 4

        XCTAssert(array == [1, 4, 3])
    }

    func testSet_Successful_AtEnd() {
        var array = [1, 2, 3]

        array[safe: 3] = 4

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testSet_Failure() {
        var array = [1, 2, 3]

        array[safe: 4] = 4

        XCTAssert(array == [1, 2, 3])
    }
}
Ivan Rep
  • 369
  • 5
  • 8
3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

Using Above mention extension return nil if anytime index goes out of bound.

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

result - nil

Paul.V
  • 386
  • 1
  • 3
  • 13
1

I have padded the array with nils in my use case:

let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]

switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
    // process last component with 5
default:
    break
}

Also check the subscript extension with safe: label by Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/

Marián Černý
  • 15,096
  • 4
  • 70
  • 83
1

The "Commonly Rejected Changes" for Swift list contains a mention of changing Array subscript access to return an optional rather than crashing:

Make Array<T> subscript access return T? or T! instead of T: The current array behavior is intentional, as it accurately reflects the fact that out-of-bounds array access is a logic error. Changing the current behavior would slow Array accesses to an unacceptable degree. This topic has come up multiple times before but is very unlikely to be accepted.

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types

So the basic subscript access will not be changing to return an optional.

However, the Swift team/community does seem open to adding a new optional-returning access pattern to Arrays, either via a function or subscript.

This has been proposed and discussed on the Swift Evolution forum here:

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871

Notably, Chris Lattner gave the idea a "+1":

Agreed, the most frequently suggested spelling for this is: yourArray[safe: idx], which seems great to me. I am very +1 for adding this.

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13

So this may be possible out of the box in some future version of Swift. I'd encourage anyone who wants it to contribute to that Swift Evolution thread.

Community
  • 1
  • 1
pkamb
  • 33,281
  • 23
  • 160
  • 191
1

To propagate why operations fail, errors are better than optionals.

public extension Collection {
  /// Ensure an index is valid before accessing an element of the collection.
  /// - Returns: The same as the unlabeled subscript, if an error is not thrown.
  /// - Throws: `AnyCollection<Element>.IndexingError`
  ///   if `indices` does not contain `index`.
  subscript(validating index: Index) -> Element {
    get throws {
      guard indices.contains(index)
      else { throw AnyCollection<Element>.IndexingError() }
      
      return self[index]
    }
  }
}

public extension AnyCollection {
  /// Thrown when `[validating:]` is called with an invalid index.
  struct IndexingError: Error { }
}
XCTAssertThrowsError(try ["", ""][validating: 2])

let collection = Array(1...10)
XCTAssertEqual(try collection[validating: 0], 1)
XCTAssertThrowsError(try collection[validating: collection.endIndex]) {
  XCTAssert($0 is AnyCollection<Int>.IndexingError)
}
1

Not sure why no one, has put up an extension that also has a setter to automatically grow the array

extension Array where Element: ExpressibleByNilLiteral {
    public subscript(safe index: Int) -> Element? {
        get {
            guard index >= 0, index < endIndex else {
                return nil
            }

            return self[index]
        }

        set(newValue) {
            if index >= endIndex {
                self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
            }

            self[index] = newValue ?? nil
        }
    }
}

Usage is easy and works as of Swift 5.1

var arr:[String?] = ["A","B","C"]

print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]

arr[safe:10] = "Z"

print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]

Note: You should understand the performance cost (both in time/space) when growing an array in swift - but for small problems sometimes you just need to get Swift to stop Swifting itself in the foot

Shaheen Ghiassy
  • 7,397
  • 3
  • 40
  • 40
0

I think this is not a good idea. It seems preferable to build solid code that does not result in trying to apply out-of-bounds indexes.

Please consider that having such an error fail silently (as suggested by your code above) by returning nil is prone to producing even more complex, more intractable errors.

You could do your override in a similar fashion you used and just write the subscripts in your own way. Only drawback is that existing code will not be compatible. I think to find a hook to override the generic x[i] (also without a text preprocessor as in C) will be challenging.

The closest I can think of is

// compile error:
if theIndex < str.count && let existing = str[theIndex]

EDIT: This actually works. One-liner!!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
    return idx < array.count ? array[idx] : nil
}

if let x: AnyObject = ifInBounds(swiftarray, 3) {
    println(x)
}
else {
    println("Out of bounds")
}
Mundi
  • 79,884
  • 17
  • 117
  • 140
  • 7
    I would disagree - the point of the optional binding is that it only succeeds if the condition is met. (For an optional, it means there is a value.) Using an `if let` in this case does not make the program more complex, nor the errors more intractable. It simply condenses the traditional two-statement `if` bounds check and actual lookup into a single-lined, condensed statement. There are cases (particularly working in a UI) where it is normal for an index to be out of bounds, like asking an `NSTableView` for the `selectedRow` without a selection. – Craig Otis Aug 15 '14 at 15:35
  • 4
    @Mundi this seems to be a comment, rather than an answer to the OP's question. – jlehr Aug 15 '14 at 15:45
  • 1
    @CraigOtis Not sure I agree. You **can** write this check succinctly in a "single-line, condensed statement", e.g. using `countElements` or as the OP did with `count`, just not in the way the language defines writing array subscripts. – Mundi Aug 15 '14 at 16:11
  • 1
    @jlehr Maybe not. It is fair game to question the intention or wisdom of a problem posed. – Mundi Aug 15 '14 at 16:12
  • @Mundi That's actually a two-liner. (I am the OP, I updated my code to reflect that the lookup is normally part of a variable assignment.) But if you know of a way to combine the bounds check and the lookup in a "single-line, condensed statement", I would be happy to see it - that's what I'm after. – Craig Otis Aug 15 '14 at 16:32
  • @CraigOtis Fair enough - let me take a crack at that... How about `if theIndex < str.count && let existent = str[theIndex] {...}`? – Mundi Aug 15 '14 at 16:36
  • That doesn't compile - you can't include variable binding in an expression in Swift. – Craig Otis Aug 15 '14 at 16:43
  • Yeah - I just realized... I will let you know if I find something, – Mundi Aug 15 '14 at 16:47
  • Check out the edit. Even the function is a one-liner. – Mundi Aug 15 '14 at 16:56
  • 2
    @Mundi Heh, especially if you later edit it to actually answer the question. :-) – jlehr Aug 15 '14 at 17:12
0

I have made a simple extension for array

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

it works perfectly as designed

Example

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 
0

You can try

if index >= 0 && index < array.count {
    print(array[index]) 
}
Jabbar
  • 590
  • 1
  • 7
  • 21
0

To be honest I faced this issue too. And from performance point of view a Swift array should be able to throw. let x = try a[y] This would be nice and understandable.

  • 1
    This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/30467268) – Runt8 Dec 02 '21 at 04:38
-1

When you only need to get values from an array and you don't mind a small performance penalty (i.e. if your collection isn't huge), there is a Dictionary-based alternative that doesn't involve (a too generic, for my taste) collection extension:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
Sorin Dolha
  • 159
  • 1
  • 8
-1

2022

infinite index access and safe idx access(returns nil in case no such idex):

public extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

    subscript (infinityIdx idx: Index) -> Element where Index == Int {
        return self[ abs(idx) % self.count ]
    }
}

but be careful, it will throw an exception in case of array/collection is empty

usage

(0...10)[safe: 11] // nil

(0...10)[infinityIdx: 11] // 0
(0...10)[infinityIdx: 12] // 1
(0...10)[infinityIdx: 21] // 0
(0...10)[infinityIdx: 22] // 1
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
-2

Swift 5 Usage

extension WKNavigationType {
    var name : String {
        get {
            let names = ["linkAct","formSubm","backForw","reload","formRelo"]
            return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
        }
    }
}

ended up with but really wanted to do generally like

[<collection>][<index>] ?? <default>

but as the collection is contextual I guess it's proper.

slashlos
  • 913
  • 9
  • 17