web-dev-qa-db-fra.com

rapide: modification des tableaux dans les dictionnaires

Comment puis-je facilement ajouter des éléments à un tableau dans un dictionnaire? Il se plaint toujours de could not find member 'append' ou could not find an overload for '+='

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]

// all of these fail
dict["key"] += 4
dict["key"].append(4) // xcode suggests dict["key"].?.append(4) which also fails
dict["key"]!.append(4)
dict["key"]?.append(4)

// however, I can do this:
var arr = dict["key"]!
arr.append(4) // this alone doesn't affect dict because it's a value type (and was copied)
dict["key"] = arr

si je viens d'affecter le tableau à un var, de le modifier puis de le réaffecter au dict, ne vais-je pas tout copier? ce ne serait ni efficace ni élégant.

27
maltalef

Swift beta 5 a ajouté cette fonctionnalité, et vous avez cloué la nouvelle méthode en quelques tentatives. Les opérateurs de déballage ! et ? transmet désormais la valeur aux opérateurs ou aux appels de méthode. Autrement dit, vous pouvez ajouter à ce tableau de l'une des manières suivantes:

dict["key"]! += [4]
dict["key"]!.append(4)
dict["key"]?.append(4)

Comme toujours, faites attention à l'opérateur que vous utilisez - forcer le déballage d'une valeur qui n'est pas dans votre dictionnaire vous donnera une erreur d'exécution:

dict["no-key"]! += [5]        // CRASH!

Alors que l'utilisation du chaînage facultatif échouera silencieusement:

dict["no-key"]?.append(5)     // Did it work? Swift won't tell you...

Idéalement, vous seriez en mesure d'utiliser le nouvel opérateur de coalescence nulle ?? pour résoudre ce deuxième cas, mais pour l'instant cela ne fonctionne pas.


Réponse de la pré-Swift beta 5:

C'est une bizarrerie de Swift qu'il n'est pas possible de faire ce que vous essayez de faire. Le problème est que la valeur de toute variable facultative est en fait une constante - même lors du déballage forcé. Si nous définissons simplement un tableau facultatif, voici ce que nous pouvons et ne pouvons pas faire:

var arr: Array<Int>? = [1, 2, 3]
arr[0] = 5
// doesn't work: you can't subscript an optional variable
arr![0] = 5
// doesn't work: constant arrays don't allow changing contents
arr += 4
// doesn't work: you can't append to an optional variable
arr! += 4
arr!.append(4)
// these don't work: constant arrays can't have their length changed

La raison pour laquelle vous rencontrez des problèmes avec le dictionnaire est que l'indexation d'un dictionnaire renvoie une valeur facultative, car il n'y a aucune garantie que le dictionnaire aura cette clé. Par conséquent, un tableau dans un dictionnaire a le même comportement que le tableau facultatif ci-dessus:

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]
dict["key"][0] = 5         // doesn't work
dict["key"]![0] = 5        // doesn't work
dict["key"] += 4           // uh uh
dict["key"]! += 4          // still no
dict["key"]!.append(4)     // nope

Si vous devez modifier quelque chose dans un tableau du dictionnaire, vous devrez obtenir une copie du tableau, le modifier et le réaffecter, comme ceci:

if var arr = dict["key"] {
    arr.append(4)
    dict["key"] = arr
}

ETA: la même technique fonctionne dans Swift beta 3, bien que les tableaux constants ne permettent plus de modifier le contenu.

31
Nate Cook

Comme solution de contournement simple, vous pouvez utiliser un NSMutableArray:

import Foundation

var dict = Dictionary<String, NSMutableArray>()
dict["key"] = [1, 2, 3] as NSMutableArray
dict["key"]!.addObject(4)

J'utilise efficacement une solution aussi simple dans mon projet:

https://github.com/gui-dos/Guigna/blob/5c02f7e70c8ee3b2265f6916c6cbbe5cd3963fb5/Guigna-Swift/Guigna/GuignaAppDelegate.Swift#L1150-L1157

6
gui_dos

La réponse acceptée contourne la possibilité beaucoup plus simple suivante, qui fonctionne également pour les anciennes versions Swift:

var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]

print(dict)

dict["key", default: [Int]()].append(4)

print(dict)

Cela imprimera:

["key": [1, 2, 3]]
["key": [1, 2, 3, 4]]

Et ça:

var dict = Dictionary<String, Array<Int>>()
dict["key", default: [Int]()].append(4)
print(dict)

imprimera:

["key": [4]]
2
Werner Altewischer

Voici ce que je disais à Nate Cook, dans les commentaires pour sa réponse de qualité. C'est ce que je considère "facilement [ajouter] des éléments à un tableau dans un dictionnaire":

dict["key"] = dict["key"]! + 4
dict["key"] = dict["key"] ? dict["key"]! + 4 : [4]

Pour l'instant, nous devons définir nous-mêmes l'opérateur +.

@infix func +<T>(array: T[], element: T) -> T[] {
    var copy = array
    copy += element
    return copy
}

Je pense que cette version supprime trop de sécurité; peut-être le définir avec un opérateur composé?

@infix func +<T>(array: T[]?, element: T) -> T[] {
    return array ? array! + element : [element]
}

dict["key"] = dict["key"] + 4

Enfin, c'est le plus clair que je puisse obtenir, mais je ne sais pas comment les valeurs/références de tableau fonctionnent dans cet exemple.

@assignment func +=<T>(inout array: T[]?, element: T) {
    array = array + element
}

dict["key"] += 5
1
Jessy