¿Búsqueda segura de matrices (verificación de límites) en Swift, a través de enlaces opcionales?

271

Si tengo una matriz en Swift e intento acceder a un índice que está fuera de los límites, hay un error de tiempo de ejecución poco sorprendente:

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

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

Sin embargo, habría pensado con todo el encadenamiento opcional y la seguridad que brinda Swift, sería trivial hacer algo como:

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

En vez de:

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

Pero este no es el caso: tengo que usar la ifinstrucción ol ' para verificar y asegurar que el índice sea menor que str.count.

Intenté agregar mi propia subscript()implementación, pero no estoy seguro de cómo pasar la llamada a la implementación original o acceder a los elementos (basados ​​en índices) sin usar la notación de subíndice:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}
Craig Otis
fuente
2
Me doy cuenta de que esto es ligeramente OT, pero también creo que sería bueno si Swift tuviera una sintaxis clara para realizar cualquier tipo de verificación de límites, incluidas las listas. Ya tenemos una palabra clave adecuada para esto, en. Entonces, por ejemplo, si X en (1,2,7) ... o si X en myArray
Maury Markowitz

Respuestas:

652

La respuesta de Alex tiene buenos consejos y solución para la pregunta, sin embargo, me topé con una mejor manera de implementar esta funcionalidad:

Swift 3.2 y más reciente

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
    }
}

Swift 3.0 y 3.1

extension Collection where Indices.Iterator.Element == Index {

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

Gracias a Hamish por encontrar la solución para Swift 3 .

Swift 2

extension CollectionType {

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

Ejemplo

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}
Nikita Kukushkin
fuente
45
Creo que esto definitivamente merece atención, buen trabajo. Me gusta el safe:nombre del parámetro incluido para garantizar la diferencia.
Craig Otis
11
A partir de Swift 2 (Xcode 7) esto necesita un pequeño ajuste:return self.indices ~= index ? self[index] : nil;
Tim
77
Con respecto a la versión Swift 3: posiblemente un indicador de solo caso de esquina, pero no obstante: hay casos en los que la versión de subíndice "segura" anterior no es segura (mientras que la versión de Swift 2 era): para los Collectiontipos donde Indicesestán no contiguo Por Setejemplo, si tuviéramos acceso a un elemento establecido por index ( SetIndex<Element>), podemos encontrar excepciones de tiempo de ejecución para los índices que son >= startIndexy < endIndex, en cuyo caso, el subíndice seguro falla (ver, por ejemplo, este ejemplo artificial ).
dfri
12
¡ADVERTENCIA! Verificar las matrices de esta manera puede ser realmente costoso. El containsmétodo iterará a través de todos los índices, haciendo de este un O (n) Una mejor manera es usar el índice y contar para verificar los límites.
Stefan Vasiljevic,
66
Para evitar generar índices e iterar sobre ellos (O (n)), es mejor usar comparaciones (O (1)): los return index >= startIndex && index < endIndex ? self[index] : nil Collectiontipos tienen startIndex, endIndexque son Comparable. Por supuesto, esto no funcionará para algunas colecciones extrañas que, por ejemplo, no tienen índices en el medio, la solución indiceses más general.
zubko
57

Si realmente quieres este comportamiento, huele como si quisieras un diccionario en lugar de una matriz. Los diccionarios regresan nilal acceder a las teclas que faltan, lo que tiene sentido porque es mucho más difícil saber si una clave está presente en un diccionario, ya que esas claves pueden ser cualquier cosa, donde en una matriz la clave debe estar en un rango de: 0a count. Y es increíblemente común iterar sobre este rango, donde puede estar absolutamente seguro de tener un valor real en cada iteración de un bucle.

Creo que la razón por la que no funciona de esta manera es una elección de diseño hecha por los desarrolladores de Swift. Toma tu ejemplo:

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

Si ya sabe que el índice existe, como lo hace en la mayoría de los casos en los que usa una matriz, este código es excelente. Sin embargo, si se accede a un subíndice podría volver nilentonces ha cambiado el tipo de retorno de Array's subscriptmétodo para ser un opcional. Esto cambia su código a:

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

Lo que significa que necesitaría desenvolver un elemento opcional cada vez que recorriera una matriz o hiciera algo más con un índice conocido, solo porque rara vez podría acceder a un índice fuera de los límites. Los diseñadores de Swift optaron por un menor desenvolvimiento de los opcionales, a expensas de una excepción de tiempo de ejecución al acceder a índices fuera de los límites. Y un bloqueo es preferible a un error lógico causado por un error nilque no esperaba en sus datos en alguna parte.

Y estoy de acuerdo con ellos. Por lo tanto, no cambiará la Arrayimplementación predeterminada porque rompería todo el código que espera valores no opcionales de las matrices.

En su lugar, podría subclase Arrayy anular subscriptpara devolver un opcional. O, más prácticamente, podría extender Arraycon un método sin subíndice que hace esto.

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
}

Actualización de Swift 3

func get(index: Int) ->T? necesita ser reemplazado por func get(index: Int) ->Element?

Alex Wayne
fuente
2
+1 (y la aceptación) por mencionar el problema al cambiar el tipo de retorno de subscript()a opcional: este fue el obstáculo principal que se enfrentó al anular el comportamiento predeterminado. (Realmente no pude hacerlo funcionar . ) Estaba evitando escribir un get()método de extensión, que es la opción obvia en otros escenarios (categorías Obj-C, ¿alguien?) Pero get(no es mucho más grande [y lo hace claro que el comportamiento puede diferir de lo que otros desarrolladores pueden esperar del operador de subíndice Swift. ¡Gracias!
Craig Otis
3
Para hacerlo aún más corto, lo uso en ();) ¡Gracias!
hyouuu
77
A partir de Swift 2.0 Tha cambiado su nombre a Element. Solo un recordatorio amistoso :)
Stas Zhukovskiy
3
Para agregar a esta discusión, otra razón por la cual la verificación de límites no está integrada en Swift para devolver un opcional es porque regresar en nillugar de causar una excepción de un índice fuera de límites sería ambiguo. Como, por ejemplo Array<String?>, también podría devolver nulo como miembro válido de la colección, no podrá diferenciar entre esos dos casos. Si tiene su propio tipo de colección que sabe que nunca puede devolver un nilvalor, es decir, es contextual para la aplicación, entonces puede extender Swift para verificar los límites de seguridad como se responde en esta publicación.
Aaron
Funciona maravillosamente
kamyFC
20

Para construir sobre la respuesta de Nikita Kukushkin, a veces es necesario asignar de forma segura a los índices de matriz, así como leerlos, es decir

myArray[safe: badIndex] = newValue

Así que aquí hay una actualización de la respuesta de Nikita (Swift 3.2) que también permite escribir de forma segura en índices de matriz mutable, agregando el nombre de parámetro seguro:.

extension Collection {
    /// Returns the element at the specified index iff 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
            }
        }
    }
}
SafeFastExpressive
fuente
2
Respuesta extremadamente subestimada! ¡Esta es la forma correcta de hacer esto!
Reid
14

Válido en Swift 2

A pesar de que esto ya ha sido respondido muchas veces, me gustaría presentar una respuesta más acorde a la moda de la programación Swift, que en palabras de Crusty es: "Piensa protocolprimero"

• ¿Qué queremos hacer?
- Obtenga un Elemento de un Arrayíndice dado solo cuando sea seguro, y de lo nilcontrario
• ¿En qué debería basar esta funcionalidad su implementación?
- Array subscripting
• ¿De dónde obtiene esta función?
- Su definición de struct Arrayen el Swiftmódulo lo tiene
• ¿Nada más genérico / abstracto?
- Adopta lo protocol CollectionTypeque también lo garantiza
• ¿Nada más genérico / abstracto?
- También adopta protocol Indexable...
• Sí, suena como lo mejor que podemos hacer. ¿Podemos extenderlo para tener esta característica que queremos?
- Pero tenemos tipos muy limitados (no Int) y propiedades (nocount) para trabajar ahora!
• Será suficiente. El stdlib de Swift está hecho bastante bien;)

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

¹: no es cierto, pero da la idea

DeFrenZ
fuente
2
Como novato de Swift, no entiendo esta respuesta. ¿Qué representa el código al final? ¿Es esa una solución, y si es así, cómo la uso realmente?
Thomas Tempelmann
3
Lo sentimos, esta respuesta ya no es válida para Swift 3, pero el proceso ciertamente lo es. La única diferencia es que ahora deberías detenerte Collectionprobablemente :)
DeFrenZ
11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) rendimiento
  • tipo seguro
  • trata correctamente los opcionales para [MyType?] (devuelve MyType ??, que se puede desenvolver en ambos niveles)
  • no conduce a problemas para conjuntos
  • código conciso

Aquí hay algunas pruebas que realicé para usted:

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?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?
thetrutz
fuente
10
  • Como las matrices pueden almacenar valores nulos, no tiene sentido devolver una nula si una llamada a la matriz [índice] está fuera de los límites.
  • Debido a que no sabemos cómo le gustaría a un usuario manejar los problemas fuera de los límites, no tiene sentido usar operadores personalizados.
  • Por el contrario, utilice el flujo de control tradicional para desenvolver objetos y garantizar la seguridad del tipo.

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
    }
}
MS Cline
fuente
1
Enfoque interesante ¿Alguna razón SafeIndexes una clase y no una estructura?
stef
8

Swift 4

Una extensión para aquellos que prefieren una sintaxis más tradicional:

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Matjan
fuente
no es necesario restringir los elementos de la matriz a ecualizables para verificar si los índices contienen su índice.
Leo Dabus el
sí, buen punto: solo sería necesario para métodos seguros adicionales como deleteObject, etc.
Matjan
5

Encontré que el arreglo seguro get, set, insert, remove es muy útil. Prefiero iniciar sesión e ignorar los errores, ya que todo lo demás pronto se vuelve difícil de administrar. Código completo abajo

/**
 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)
            }
        }
    }
}

Pruebas

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
fuente
3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

El uso de la extensión mencionada anteriormente devuelve nil si el índice sale del límite en cualquier momento.

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

resultado - nulo

Vinayak Pal
fuente
3

Me doy cuenta de que esta es una vieja pregunta. Estoy usando Swift5.1 en este punto, ¿el OP era para Swift 1 o 2?

Necesitaba algo como esto hoy, pero no quería agregar una extensión a escala completa para un solo lugar y quería algo más funcional (¿más seguro para subprocesos?). Tampoco necesitaba proteger contra índices negativos, solo aquellos que podrían haber pasado el final de una matriz:

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

Para aquellos que discuten sobre Secuencias con cero, ¿qué hacen con el firstylast propiedades que nulo cambio de colecciones vacías?

Me gustó esto porque podía agarrar cosas existentes y usarlas para obtener el resultado que quería. También sé que dropFirst (n) no es una copia de colección completa, solo una porción. Y luego el comportamiento ya existente de primero se hace cargo de mí.

Travis Griggs
fuente
1

Creo que esta no es una buena idea. Parece preferible construir código sólido que no resulte en intentar aplicar índices fuera de los límites.

Tenga en cuenta que si dicho error falla silenciosamente (como lo sugiere su código anterior) al regresar niles propenso a producir errores aún más complejos e intratables.

Puede hacer su anulación de una manera similar a la que usó y simplemente escribir los subíndices a su manera. El único inconveniente es que el código existente no será compatible. Creo que encontrar un gancho para anular el genérico x [i] (también sin un preprocesador de texto como en C) será un desafío.

Lo más cerca que puedo pensar es

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

EDITAR : Esto realmente funciona. ¡¡Un trazador de líneas!!

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
fuente
66
No estoy de acuerdo: el punto del enlace opcional es que solo tiene éxito si se cumple la condición. (Para un opcional, significa que hay un valor). El uso de un if leten este caso no hace que el programa sea más complejo, ni los errores más intratables. Simplemente condensa la ifverificación tradicional de límites de dos declaraciones y la búsqueda real en una declaración condensada de una sola línea. Hay casos (sobre todo de trabajo en una interfaz de usuario), donde es normal para un índice que se va fuera de límites, como pedir una NSTableViewpara el selectedRowy sin una selección.
Craig Otis
3
@Mundi esto parece ser un comentario, en lugar de una respuesta a la pregunta del OP.
jlehr
1
@CraigOtis No estoy seguro de estar de acuerdo. Usted puede escribir esta comprobación sucintamente en un "de una sola línea, estado condensado", por ejemplo, usando countElementso como el hizo con OP count, pero no en la forma en que define el idioma de escritura subíndices de matriz.
Mundi
1
@jlehr Quizás no. Es un juego justo cuestionar la intención o la sabiduría de un problema planteado.
Mundi
2
@Mundi Heh, especialmente si luego lo editas para responder la pregunta. :-)
jlehr
1

He rellenado la matriz con nils en mi caso de uso:

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
}

Compruebe también la extensión del subíndice con safe:etiqueta de Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/

Marián Černý
fuente
0

La lista "Cambios comúnmente rechazados" para Swift contiene una mención de cambiar el acceso al subíndice de matriz para devolver un elemento opcional en lugar de bloquearse:

Haga que el Array<T>acceso al subíndice vuelva T?o en T!lugar de T: El comportamiento de la matriz actual es intencional , ya que refleja con precisión el hecho de que el acceso a la matriz fuera de los límites es un error lógico. Cambiar el comportamiento actual retrasaría los Arrayaccesos a un grado inaceptable. Este tema ha aparecido varias veces antes, pero es muy poco probable que sea aceptado.

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

Por lo tanto, el acceso básico al subíndice no cambiará para devolver un opcional.

Sin embargo, el equipo / comunidad Swift parece estar abierto a agregar un nuevo patrón de acceso de retorno opcional a las matrices, ya sea a través de una función o subíndice.

Esto ha sido propuesto y discutido en el foro Swift Evolution aquí:

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

En particular, Chris Lattner le dio a la idea un "+1":

De acuerdo, la ortografía sugerida con más frecuencia para esto es: yourArray[safe: idx] lo que me parece genial. Soy muy +1 por agregar esto.

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

Por lo tanto, esto puede ser posible fuera de la caja en alguna versión futura de Swift. Animaría a cualquiera que quiera que contribuya a ese hilo de Swift Evolution.

pkamb
fuente
0

Para propagar por qué fallan las operaciones, los errores son mejores que los opcionales. Los subíndices no pueden arrojar errores, por lo que debe ser un método.

public extension Collection {
  /// - Returns: same as subscript, if index is in bounds
  /// - Throws: CollectionIndexingError
  func element(at index: Index) throws -> Element {
    guard indices.contains(index)
    else { throw CollectionIndexingError() }

    return self[index]
  }
}

/// Thrown when `element(at:)` is called with an invalid index.
public struct CollectionIndexingError: Error { }
XCTAssertThrowsError( try ["🐾", "🥝"].element(at: 2) )

let optionals = [1, 2, nil]
XCTAssertEqual(try optionals.element(at: 0), 1)

XCTAssertThrowsError( try optionals.element(at: optionals.endIndex) )
{ XCTAssert($0 is CollectionIndexingError) }
Jessy
fuente
0

No estoy seguro de por qué nadie ha puesto una extensión que también tiene un setter para hacer crecer automáticamente la matriz

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
        }
    }
}

El uso es fácil y funciona a partir de 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")]

Nota: Debe comprender el costo de rendimiento (tanto en tiempo / espacio) cuando crece una matriz en Swift, pero para pequeños problemas a veces solo necesita hacer que Swift deje de Swift en el pie

Shaheen Ghiassy
fuente
-1

He hecho una extensión simple para 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
        }
    }

}

funciona perfectamente como está diseñado

Ejemplo

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 
Mohamed Deux
fuente
-1

Uso rápido de 5

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

terminó con pero realmente quería hacer en general como

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

pero como la colección es contextual, supongo que es adecuada.

slashlos
fuente
¿Cómo es esta respuesta diferente de la aceptada? En cuanto a mí, se ve exactamente igual (duplicado).
Legonaftik
-1

Cuando solo necesita obtener valores de una matriz y no le importa una pequeña penalización de rendimiento (es decir, si su colección no es enorme), existe una alternativa basada en el diccionario que no implica (una muy genérica, para mi gusto) extensión de la colección:

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