3

I do for example this request:

mannschaft18teamid = results2.data.table[17].team_id 

let's say the 17th array element doesn't exist. How can I set the standard value of it if it does not exist? I want the value to be as a string "not existing".

Maybe like this?:

mannschaft18teamid = results2.data.table[17].team_id ?? "not existing"

It does not work. The simulator crashes with: Thread 1: Fatal error: Index out of range.

It does not even need to set a value. It is also ok if it just ignores that line.

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
submariner
  • 308
  • 1
  • 5
  • 23

4 Answers4

4

If results2.data.table is an array, try this:

let index = 17
if results2.data.table.indices.contains(index) {
    mannschaft18teamid = results2.data.table[index].team_id
} else {
    mannschaft18teamid = "not existing"
}

You're getting the Index out of range error, so you should use indices.contains(index) to check if the index exists. More info here.

aheze
  • 24,434
  • 8
  • 68
  • 125
4

If you want this syntax, you could extend RandomAccessCollection and implement a subscribt that returns an optional value from the array:

extension RandomAccessCollection {
    subscript(index: Index) -> Element? {
        self.indices.contains(index) ? (self[index] as Element) : nil
    }
}

This way you could do something like this:

let arr = ["a", "b", "c"]
print(arr[99] ?? "not existing") // "not existing"

You'd have to be careful to make sure that an Optional type is inferred from the context, like with ?? or with optional chaining arr[99]?.prop, because the following would still behave normally, i.e. will fail with Index out of range:

let v = arr[99] // error

Alternatively, you can define a subscript with a default value (like Dictionary does):

extension RandomAccessCollection {
    subscript(index: Index, default defaultValue: @autoclosure () -> Element) -> Element {
        self.indices.contains(index) ? self[index] : defaultValue()
    }
}
let arr = [1,2,3]
print(arr[9, default: 99]) // 99
New Dev
  • 48,427
  • 12
  • 87
  • 129
2

If you don't need it to set it any value you should probably use a guard statement to get the element at index.

guard results2.data.table.count >= 18 else {
    // Bad path, early exit scope
    return 
}

// Happy path, keep going
mannschaft18teamid = results2.data.table[17].team_id

But you should keep in mind, guards are for early exit from scope in unexpected situations so anything after that will not be executed. If you want execution to keep going then you should use if let:

if let results2.data.table.count >= 18 {
    // Index exists, do stuff
   mannschaft18teamid = results2.data.table[17].team_id
}

If you want to keep using nil coalescing operator (??) to provide a default value, then you should implement an extension to Collections to safely access items. You can read other answers or refer to this answer.

banderson
  • 65
  • 1
  • 11
1

It would be better to use an optional extension which returns nil if the element at the index doesn't exist.

Extension

extension Collection where Indices.Iterator.Element == Index {
    subscript (optional index: Index) -> Iterator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Usage:

mannschaft18teamid = results2.data.table[optional: 17]?.team_id ?? "not existing"
Frankenstein
  • 15,732
  • 4
  • 22
  • 47