Recherche de tableau sécurisée (vérifiée aux limites) dans Swift, via des liaisons facultatives?

271

Si j'ai un tableau dans Swift et que j'essaie d'accéder à un index hors limites, il y a une erreur d'exécution sans surprise:

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

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

Cependant, j'aurais pensé qu'avec tout le chaînage et la sécurité optionnels que Swift apporte, il serait trivial de faire quelque chose comme:

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

Au lieu de:

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

Mais ce n'est pas le cas - je dois utiliser l' ifinstruction ol ' pour vérifier et m'assurer que l'indice est inférieur à str.count.

J'ai essayé d'ajouter ma propre subscript()implémentation, mais je ne sais pas comment passer l'appel à l'implémentation d'origine, ou pour accéder aux éléments (basés sur un index) sans utiliser la notation en indice:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}
Craig Otis
la source
2
Je me rends compte que c'est légèrement OT, mais je pense également que ce serait bien si Swift avait une syntaxe claire pour effectuer toute sorte de vérification des limites, y compris les listes. Nous avons déjà un mot-clé approprié pour cela, dans. Donc, par exemple, si X dans (1,2,7) ... ou si X dans myArray
Maury Markowitz

Réponses:

652

La réponse d'Alex contient de bons conseils et une bonne solution pour la question, cependant, je suis tombé sur une meilleure façon de mettre en œuvre cette fonctionnalité:

Swift 3.2 et plus récent

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

Nous remercions Hamish d'avoir trouvé la solution pour 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
    }
}

Exemple

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}
Nikita Kukushkin
la source
45
Je pense que cela mérite vraiment l'attention - beau travail. J'aime le safe:nom du paramètre inclus pour assurer la différence.
Craig Otis
11
À partir de Swift 2 (Xcode 7), cela nécessite un petit ajustement:return self.indices ~= index ? self[index] : nil;
Tim
7
En ce qui concerne la version Swift 3: peut-être une invite de coin uniquement, mais une invite néanmoins: il y a des cas où la version d'indice "sûre" ci-dessus n'est pas sûre (alors que la version Swift 2 l'était): pour les Collectiontypes où les Indicessont pas contiguë. Par Setexemple, si nous devions accéder à un élément set par index ( SetIndex<Element>), nous pouvons exécuter des exceptions d'exécution pour les index qui sont >= startIndexet < endIndex, dans ce cas, l'indice sécurisé échoue (voir par exemple cet exemple artificiel ).
dfri
12
AVERTISSEMENT! Vérifier les tableaux de cette façon peut être très coûteux. La containsméthode parcourra tous les indices, ce qui en fera un O (n). Une meilleure façon est d'utiliser l'index et le comptage pour vérifier les limites.
Stefan Vasiljevic
6
Pour éviter de générer des indices et de les itérer (O (n)), il est préférable d'utiliser des comparaisons (O (1)): les return index >= startIndex && index < endIndex ? self[index] : nil Collectiontypes ont startIndex, endIndexqui le sont Comparable. Bien sûr, cela ne fonctionnera pas pour certaines collections étranges qui, par exemple, n'ont pas d'indices au milieu, la solution avec indicesest plus générale.
zubko
57

Si vous voulez vraiment ce comportement, cela sent comme si vous vouliez un dictionnaire au lieu d'un tableau. Les dictionnaires reviennent nillors de l'accès aux clés manquantes, ce qui est logique car il est beaucoup plus difficile de savoir si une clé est présente dans un dictionnaire car ces clés peuvent être n'importe quoi, où dans un tableau la clé doit être dans une plage de: 0à count. Et il est incroyablement courant d'itérer sur cette plage, où vous pouvez être absolument sûr d' avoir une valeur réelle à chaque itération d'une boucle.

Je pense que la raison pour laquelle cela ne fonctionne pas de cette façon est un choix de conception fait par les développeurs de Swift. Prenez votre exemple:

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

Si vous savez déjà que l'index existe, comme vous le faites dans la plupart des cas où vous utilisez un tableau, ce code est parfait. Cependant, si l'accès à un indice peut éventuellement renvoyer, nilvous avez modifié le type de retour de Arrayla subscriptméthode de en option. Cela change votre code en:

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

Ce qui signifie que vous devrez déballer une option à chaque fois que vous itérerez dans un tableau, ou que vous ferez quoi que ce soit d'autre avec un index connu, simplement parce que vous pourriez rarement accéder à un index hors limites. Les concepteurs de Swift ont opté pour moins de déballage des options, au détriment d'une exception d'exécution lors de l'accès aux index hors limites. Et un plantage est préférable à une erreur logique causée par un résultat nilinattendu quelque part dans vos données.

Et je suis d'accord avec eux. Vous ne modifierez donc pas l' Arrayimplémentation par défaut, car vous casseriez tout le code qui attend des valeurs non facultatives des tableaux.

Au lieu de cela, vous pouvez sous Array-classer et remplacer subscriptpour renvoyer une option. Ou, plus concrètement, vous pouvez étendre Arrayavec une méthode non-indice qui fait cela.

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
}

Mise à jour Swift 3

func get(index: Int) ->T? doit être remplacé par func get(index: Int) ->Element?

Alex Wayne
la source
2
+1 (et l'acceptation) pour avoir mentionné le problème de la modification du type de retour de subscript()en option - c'était le principal obstacle rencontré pour remplacer le comportement par défaut. (Je ne pouvais pas vraiment le faire fonctionner du tout. ) J'évitais d'écrire une get()méthode d'extension, ce qui est le choix évident dans d'autres scénarios (catégories Obj-C, n'importe qui?) Mais get(n'est pas beaucoup plus grand que [, et le fait clairement que le comportement peut différer de ce que les autres développeurs peuvent attendre de l'opérateur d'indice Swift. Je vous remercie!
Craig Otis
3
Pour le raccourcir encore plus, j'utilise chez ();) Merci!
hyouuu
7
À partir de Swift 2.0 Ta été renommé Element. Juste un rappel amical :)
Stas Zhukovskiy
3
Pour ajouter à cette discussion, une autre raison pour laquelle la vérification des limites n'est pas intégrée dans Swift pour renvoyer une option est que le retour nilau lieu de provoquer une exception à partir d'un index hors limites serait ambigu. Comme par exemple, Array<String?>pourrait également renvoyer nil en tant que membre valide de la collection, vous ne pourrez pas faire la différence entre ces deux cas. Si vous avez votre propre type de collection et que vous savez qu'il ne peut jamais renvoyer de nilvaleur, c'est-à-dire qu'il est contextuel à l'application, vous pouvez étendre Swift pour une vérification des limites sécurisées, comme indiqué dans cet article.
Aaron
Fonctionne à merveille
kamyFC
20

Pour s'appuyer sur la réponse de Nikita Kukushkin, vous devez parfois attribuer en toute sécurité des index de tableaux ainsi que les lire, c'est-à-dire

myArray[safe: badIndex] = newValue

Voici donc une mise à jour de la réponse de Nikita (Swift 3.2) qui permet également d'écrire en toute sécurité dans les index de tableaux mutables, en ajoutant le nom du paramètre safe :.

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
la source
2
Réponse extrêmement sous-estimée! C'est la bonne façon de procéder!
Reid
14

Valide dans Swift 2

Même si cela a déjà été répondu à maintes reprises, je voudrais présenter une réponse plus conforme à la tendance de la programmation Swift, qui, selon les mots de Crusty¹, est: "Pensez d' protocolabord"

• Que voulons-nous faire?
- Obtenir un élément d'un Arrayindex donné uniquement lorsqu'il est sûr, et nilsinon
• Sur quoi cette fonctionnalité doit-elle baser son implémentation?
- Array subscripting
• D'où obtient-elle cette fonctionnalité?
- Sa définition struct Arraydans le Swiftmodule l'a
• Rien de plus générique / abstrait?
- Il adopte protocol CollectionTypece qui le garantit aussi
• Rien de plus générique / abstrait?
- Il adopte protocol Indexableaussi ...
• Ouaip, ça sonne comme le mieux qu'on puisse faire. Pouvons-nous alors l'étendre pour avoir cette fonctionnalité que nous voulons?
- Mais nous avons des types Intet des propriétés très limités (non )count) pour travailler avec maintenant!
• Ce sera suffisant. Le stdlib de Swift est plutôt bien fait;)

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

¹: pas vrai, mais ça donne l'idée

DeFrenZ
la source
2
En tant que débutant Swift, je ne comprends pas cette réponse. Que représente le code à la fin? Est-ce une solution, et si oui, comment dois-je l'utiliser?
Thomas Tempelmann
3
Désolé, cette réponse n'est plus valable pour Swift 3, mais le processus l'est certainement. La seule différence est que maintenant vous devriez vous arrêter Collectionprobablement :)
DeFrenZ
11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) performance
  • type sûr
  • traite correctement les options pour [MyType?] (renvoie MyType ??, qui peut être déballé aux deux niveaux)
  • ne pose pas de problème pour les Sets
  • code concis

Voici quelques tests que j'ai effectués pour vous:

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
la source
10
  • Étant donné que les tableaux peuvent stocker des valeurs nulles, il n'est pas logique de renvoyer un zéro si un appel de tableau [index] est hors limites.
  • Parce que nous ne savons pas comment un utilisateur souhaite gérer les problèmes hors limites, il n'est pas logique d'utiliser des opérateurs personnalisés.
  • En revanche, utilisez le flux de contrôle traditionnel pour déballer les objets et assurer la sécurité du type.

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

  let item = array[safeIndex: index] 

si 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
la source
1
Approche intéressante. Une raison SafeIndexest une classe et non une structure?
stef
8

Swift 4

Une extension pour ceux qui préfèrent une syntaxe plus traditionnelle:

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Matjan
la source
vous n'avez pas besoin de contraindre les éléments du tableau à équivalents pour vérifier si les index contiennent son index.
Leo Dabus
oui - bon point - cela ne serait nécessaire que pour des méthodes sûres supplémentaires comme deleteObject, etc.
Matjan
5

J'ai trouvé le tableau sécurisé get, set, insert, remove très utile. Je préfère me connecter et ignorer les erreurs car tout le reste devient rapidement difficile à gérer. Code complet ci-dessous

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

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

L'utilisation de l'extension mentionnée ci-dessus renvoie nil si à tout moment l'index sort des limites.

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

résultat - nul

Vinayak Pal
la source
3

Je me rends compte que c'est une vieille question. J'utilise Swift5.1 à ce stade, l'OP était pour Swift 1 ou 2?

J'avais besoin de quelque chose comme ça aujourd'hui, mais je ne voulais pas ajouter une extension pleine échelle pour un seul endroit et je voulais quelque chose de plus fonctionnel (plus sûr pour les threads?). Je n'avais pas non plus besoin de me protéger contre les indices négatifs, juste ceux qui pourraient être au-delà de la fin d'un tableau:

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

Pour ceux qui discutent des séquences avec nil, que faites-vous des propriétés firstet lastqui renvoient nil pour les collections vides?

J'ai aimé cela parce que je pouvais simplement saisir des choses existantes et les utiliser pour obtenir le résultat que je voulais. Je sais également que dropFirst (n) n'est pas une copie de collection entière, juste une tranche. Et puis le comportement déjà existant du premier prend le relais pour moi.

Travis Griggs
la source
1

Je pense que ce n'est pas une bonne idée. Il semble préférable de construire du code solide qui n'entraîne pas d'essayer d'appliquer des index hors limites.

Veuillez considérer qu'avoir une telle erreur échoue silencieusement (comme suggéré par votre code ci-dessus) en retournant nilest susceptible de produire des erreurs encore plus complexes et plus intraitables.

Vous pouvez effectuer votre remplacement d'une manière similaire à celle que vous avez utilisée et simplement écrire les indices à votre manière. Le seul inconvénient est que le code existant ne sera pas compatible. Je pense que trouver un crochet pour remplacer le x [i] générique (également sans préprocesseur de texte comme en C) sera difficile.

Le plus proche auquel je peux penser est

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

EDIT : Cela fonctionne réellement. Bon mot!!

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
la source
6
Je ne suis pas d'accord - le but de la liaison facultative est qu'elle ne réussit que si la condition est remplie. (Pour une option, cela signifie qu'il y a une valeur.) L'utilisation d'un if letdans ce cas ne rend pas le programme plus complexe, ni les erreurs plus insolubles. Il condense simplement la ifvérification des limites à deux instructions traditionnelle et la recherche réelle en une instruction condensée à simple ligne. Il y a des cas (en particulier dans une interface utilisateur) où il est normal qu'un index soit hors limites, comme demander un NSTableViewpour selectedRowsans sélection.
Craig Otis
3
@Mundi, cela semble être un commentaire plutôt qu'une réponse à la question du PO.
jlehr
1
@CraigOtis Je ne suis pas sûr d'être d'accord. Vous pouvez écrire cette vérification de manière succincte dans une "instruction condensée sur une seule ligne", par exemple en utilisant countElementsou comme l'OP l'a fait count, mais pas de la manière dont le langage définit l'écriture des indices de tableau.
Mundi
1
@jlehr Peut-être pas. Il est juste de remettre en question l'intention ou la sagesse d'un problème posé.
Mundi
2
@Mundi Heh, surtout si vous le modifiez plus tard pour répondre réellement à la question. :-)
jlehr
1

J'ai complété le tableau avec nils dans mon cas d'utilisation:

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
}

Vérifiez également l'extension d'indice avec l' safe:étiquette par Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/

Marián Černý
la source
0

La liste des "Changements fréquemment rejetés" pour Swift contient une mention concernant la modification de l' accès aux indices de tableau pour renvoyer une option plutôt que de planter:

Rendre l' Array<T>accès en indice T?ou à la T!place de T: Le comportement actuel du tableau est intentionnel , car il reflète avec précision le fait que l'accès au tableau hors limites est une erreur logique. Changer le comportement actuel ralentirait les Arrayaccès à un degré inacceptable. Ce sujet a été soulevé plusieurs fois auparavant, mais il est très peu probable qu'il soit accepté.

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

Ainsi, l'accès en indice de base ne changera pas pour renvoyer une option.

Cependant, l'équipe / communauté Swift semble ouverte à l' ajout d' un nouveau modèle d'accès à retour facultatif aux tableaux, via une fonction ou un indice.

Ceci a été proposé et discuté sur le forum Swift Evolution ici:

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

Notamment, Chris Lattner a donné à l'idée un "+1":

D'accord, l'orthographe la plus fréquemment suggérée est:, ce yourArray[safe: idx]qui me semble génial. Je suis très +1 pour avoir ajouté ceci.

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

Cela peut donc être possible dès la sortie de la boîte dans une future version de Swift. J'encourage tous ceux qui le souhaitent à contribuer à ce fil Swift Evolution.

pkamb
la source
0

Pour propager pourquoi les opérations échouent, les erreurs sont meilleures que les options. Les abonnements ne peuvent pas générer d'erreurs, il faut donc que ce soit une méthode.

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
la source
0

Je ne sais pas pourquoi personne n'a mis en place une extension qui dispose également d'un setter pour agrandir automatiquement le tableau

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

L'utilisation est facile et fonctionne à 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")]

Remarque: vous devez comprendre le coût des performances (à la fois dans le temps et dans l'espace) lors de la croissance d'un tableau dans swift - mais pour les petits problèmes, il vous suffit parfois de faire en sorte que Swift arrête de se déplacer rapidement dans le pied

Shaheen Ghiassy
la source
-1

J'ai fait une extension simple pour le tableau

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

}

il fonctionne parfaitement comme prévu

Exemple

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

Utilisation de Swift 5

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

fini avec mais voulait vraiment faire généralement comme

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

mais comme la collection est contextuelle, je suppose qu'elle est appropriée.

slashlos
la source
En quoi cette réponse est-elle différente de la réponse acceptée? Quant à moi, il a exactement la même apparence (en double).
Legonaftik
-1

Lorsque vous avez seulement besoin d' obtenir des valeurs d'un tableau et que cela ne vous dérange pas une petite pénalité de performance (c'est-à-dire si votre collection n'est pas énorme), il existe une alternative basée sur le dictionnaire qui n'implique pas (trop générique, pour mon goût) extension de collection:

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