133 votes

Mapper un tableau d'objets vers Dictionary dans Swift

J'ai un tableau d'objets Person :

 class Person {
   let name:String
   let position:Int
}
 

et le tableau est:

 let myArray = [p1,p1,p3]
 

Je veux mapper myArray pour être un dictionnaire de [position:name] la solution classique est:

 var myDictionary =  [Int:String]()

for person in myArray {
    myDictionary[person.position] = person.name
}
 

Y a-t-il une manière élégante par Swift de le faire avec l'approche fonctionnelle map , flatMap ... ou tout autre style Swift moderne

205voto

possen Points 595

En Swift 4 vous pouvez faire l'approche de @ Tj3n plus proprement et plus efficacement en utilisant la version into de reduce Il supprime le dictionnaire temporaire et la valeur de retour pour qu'il soit plus rapide et plus facile à lire.

 struct Person { 
    let name: String
    let position: Int
}
let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]
let myDict = myArray.reduce(into: [Int: String]()) {
    $0[$1.position] = $1.name
}
print(myDict)
 

Remarque: ne fonctionne pas dans Swift 3.

152voto

Tj3n Points 6697

D'accord map n'est pas un bon exemple de cela, car c'est exactement comme le bouclage, vous pouvez utiliser reduce place, il a fallu que chacun de vos objets se combine et se transforme en valeur unique:

 let myDictionary = myArray.reduce([Int: String]()) { (dict, person) -> [Int: String] in
    var dict = dict
    dict[person.position] = person.name
    return dict
}

//[2: "b", 3: "c", 1: "a"]
 

118voto

Mackie Messer Points 3129

Depuis Swift 4 vous pouvez le faire très facilement. Il y a deux nouveaux initialiseurs que construire un dictionnaire à partir d'une séquence de n-uplets (paires de clés et de valeurs). Si les clés sont uniques, vous pouvez effectuer les opérations suivantes:

let persons = [Person(name: "Franz", position: 1),
               Person(name: "Heinz", position: 2),
               Person(name: "Hans", position: 3)]

Dictionary(uniqueKeysWithValues: persons.map { ($0.position, $0.name) })

=> [1: "Franz", 2: "Heinz", 3: "Hans"]

Cela échouera avec une erreur d'exécution si une touche est dupliqué. Dans ce cas, vous pouvez utiliser cette version:

let persons = [Person(name: "Franz", position: 1),
               Person(name: "Heinz", position: 2),
               Person(name: "Hans", position: 1)]

Dictionary(persons.map { ($0.position, $0.name) }) { _, last in last }

=> [1: "Hans", 2: "Heinz"]

Il se comporte comme votre boucle for. Si vous ne voulez pas "écraser" les valeurs et le bâton à la première cartographie, vous pouvez utiliser ceci:

Dictionary(persons.map { ($0.position, $0.name) }) { first, _ in first }

=> [1: "Franz", 2: "Heinz"]

Swift 4.2 ajoute un troisième initialiseur de groupes d'éléments de la séquence dans un dictionnaire. Les clés de dictionnaire sont obtenus par une fermeture. Éléments avec la même clé sont mis dans un tableau dans le même ordre que dans la séquence. Cela vous permet d'obtenir le même résultat que ci-dessus. Par exemple:

Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.last! }

=> [1: Person(name: "Hans", position: 1), 2: Person(name: "Heinz", position: 2)]

Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.first! }

=> [1: Person(name: "Franz", position: 1), 2: Person(name: "Heinz", position: 2)]

9voto

Shadow Of Points 4283

Vous pouvez écrire un initialiseur personnalisé pour le type Dictionary , par exemple à partir de tuples:

 extension Dictionary {
    public init(keyValuePairs: [(Key, Value)]) {
        self.init()
        for pair in keyValuePairs {
            self[pair.0] = pair.1
        }
    }
}
 

puis utilisez map pour votre tableau de Person :

 var myDictionary = Dictionary(keyValuePairs: myArray.map{($0.position, $0.name)})
 

2voto

David Points 818

Vous pouvez utiliser une fonction de réduction. J'ai d'abord créé un initialiseur désigné pour la classe Person

 class Person {
  var name:String
  var position:Int

  init(_ n: String,_ p: Int) {
    name = n
    position = p
  }
}
 

Plus tard, j'ai initialisé un tableau de valeurs

 let myArray = [Person("Bill",1), 
               Person("Steve", 2), 
               Person("Woz", 3)]
 

Et enfin, la variable dictionnaire a le résultat:

 let dictionary = myArray.reduce([Int: Person]()){
  (total, person) in
  var totalMutable = total
  totalMutable.updateValue(person, forKey: total.count)
  return totalMutable
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X