В Swift есть ли способ проверить, существует ли индекс в массиве без фатальной ошибки?
Я надеялся, что смогу сделать что-то вроде этого:
let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
// this wouldn't run
println(str2)
} else {
// this would be run
}
Но я получаю
фатальная ошибка: индекс массива вне допустимого диапазона
index < array.count
?ArraySlice
, первый индекс не будет равен 0, поэтомуindex >= 0
проверка не будет достаточно хорошей..indices
вместо этого работает в любом случае.Расширение типа:
extension Collection { subscript(optional i: Index) -> Iterator.Element? { return self.indices.contains(i) ? self[i] : nil } }
Используя это, вы получите необязательное значение при добавлении ключевого слова optional к вашему индексу, что означает, что ваша программа не выйдет из строя, даже если индекс находится вне диапазона. В вашем примере:
let arr = ["foo", "bar"] let str1 = arr[optional: 1] // --> str1 is now Optional("bar") if let str2 = arr[optional: 2] { print(str2) // --> this still wouldn't run } else { print("No string found at that index") // --> this would be printed }
источник
optional
параметра in. Благодарность!Просто проверьте, меньше ли индекс размера массива:
if 2 < arr.count { ... } else { ... }
источник
Добавьте немного сахара-удлинителя:
extension Collection { subscript(safe index: Index) -> Iterator.Element? { guard indices.contains(index) else { return nil } return self[index] } } if let item = ["a", "b", "c", "d"][safe: 3] { print(item) }//Output: "d" //or with guard: guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else {return} print(anotherItem) // "d"
Повышает удобочитаемость при
if let
кодировании стиля в сочетании с массивамиисточник
Вы можете переписать это более безопасным способом, чтобы проверить размер массива, и использовать тернарное условное выражение:
if let str2 = (arr.count > 2 ? arr[2] : nil) as String?
источник
if
операторов вместо одногоif
в исходном коде. В моем коде второй заменяетсяif
условным оператором, что позволяет сохранить одинelse
вместо принудительного использования двух отдельныхelse
блоков.if
из OP окажется внутри ветки «then» ответа Антонио, поэтому будет два вложенныхif
s. Я рассматриваю код OP в качестве небольшого примера, поэтому предполагаю, что ему все еще нужен файлif
. Я согласен с вами, что в его примереif
это не нужно. Но опять же, весь оператор бессмысленен, потому что OP знает, что у массива недостаточно длины и что нет ни одного из его элементовnil
, поэтому он может удалитьif
и оставить только егоelse
блок.Расширение Swift 4:
Для меня я предпочитаю подобный метод.
// MARK: - Extension Collection extension Collection { /// Get at index object /// /// - Parameter index: Index of object /// - Returns: Element at index or nil func get(at index: Index) -> Iterator.Element? { return self.indices.contains(index) ? self[index] : nil } }
Спасибо @Benno Kress
источник
extension Array { func isValidIndex(_ index : Int) -> Bool { return index < self.count } }
let array = ["a","b","c","d"] func testArrayIndex(_ index : Int) { guard array.isValidIndex(index) else { print("Handle array index Out of bounds here") return } }
Мне нужно обрабатывать indexOutOfBounds .
источник
Утверждение, существует ли индекс массива:
Эта методика отлично подходит, если вы не хотите добавлять сахар для расширения:
let arr = [1,2,3] if let fourthItem = (3 < arr.count ? arr[3] : nil ) { Swift.print("fourthItem: \(fourthItem)") }else if let thirdItem = (2 < arr.count ? arr[2] : nil) { Swift.print("thirdItem: \(thirdItem)") } //Output: thirdItem: 3
источник
Расширение Swift 4 и 5:
На мой взгляд, это самое безопасное решение:
public 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 } } } }
Пример:
let array = ["foo", "bar"] if let str = array[safe: 1] { print(str) // "bar" } else { print("index out of range") }
источник