J'ai un grand tableau et j'ai besoin d'y accéder par une clé (une recherche) donc j'ai besoin de créer un dictionnaire. Existe-t-il une fonction intégrée dans Swift 3.0), ou dois-je l'écrire moi-même?
D'abord, j'en aurai besoin pour une classe avec la clé "String" et plus tard, je pourrai peut-être écrire une version de modèle à usage général (tous les types de données et de clés).
Remarque pour 2019. Ceci est maintenant simplement intégré à Swift, uniqueKeysWithValues
et des appels similaires.
Je pense que vous cherchez quelque chose comme ça:
extension Array {
public func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key:Element] {
var dict = [Key:Element]()
for element in self {
dict[selectKey(element)] = element
}
return dict
}
}
Vous pouvez maintenant faire:
struct Person {
var name: String
var surname: String
var identifier: String
}
let arr = [Person(name: "John", surname: "Doe", identifier: "JOD"),
Person(name: "Jane", surname: "Doe", identifier: "JAD")]
let dict = arr.toDictionary { $0.identifier }
print(dict) // Result: ["JAD": Person(name: "Jane", surname: "Doe", identifier: "JAD"), "JOD": Person(name: "John", surname: "Doe", identifier: "JOD")]
Si vous souhaitez que votre code soit plus général, vous pouvez même ajouter cette extension sur Sequence
au lieu de Array
:
extension Sequence {
public func toDictionary<Key: Hashable>(with selectKey: (Iterator.Element) -> Key) -> [Key:Iterator.Element] {
var dict: [Key:Iterator.Element] = [:]
for element in self {
dict[selectKey(element)] = element
}
return dict
}
}
Notez que cela provoque l'itération de la séquence et peut avoir des effets secondaires dans certains cas.
Est-ce que c'est (in Swift 4)?
let dict = Dictionary(uniqueKeysWithValues: array.map{ ($0.key, $0) })
(Remarque - si vous craignez des clés en double, vous pouvez utiliser la varianteDictionary#keysAndValues#uniquingKeysWith
qui vous permet de gérer les dupes.)
Sur Swift 4, vous pouvez y parvenir en utilisant l'initialiseur du dictionnaire grouping:by:
Par exemple: vous avez nommé la classe [~ # ~] a [~ # ~]
class A {
var name: String
init(name: String) {
self.name = name
}
// .
// .
// .
// other declations and implementions
}
Ensuite, vous avez un tableau d'objets de type [~ # ~] a [~ # ~]
let a1 = A(name: "Joy")
let a2 = A(name: "Ben")
let a3 = A(name: "Boy")
let a4 = A(name: "Toy")
let a5 = A(name: "Tim")
let array = [a1, a2, a3, a4, a5]
Supposons que vous souhaitiez créer un dictionnaire en regroupant tous les noms par leur première lettre. Vous utilisez Swifts Dictionary(grouping:by:)
pour y parvenir
let dictionary = Dictionary(grouping: array, by: { $0.name.first! })
// this will give you a dictionary
// ["J": [a1], "B": [a2, a3], "T": [a4, a5]]
Notez cependant que le dictionnaire "dictionnaire" résultant est de type
[String : [A]]
il n'est pas de type
[String : A]
comme vous pouvez vous attendre. (Utilisez #uniqueKeysWithValues
Pour atteindre ce dernier point.)
Comme d'autres l'ont déjà dit, nous devons comprendre quelles sont les clés.
Cependant, j'essaie de fournir une solution à mon interprétation de votre question.
struct User {
let id: String
let firstName: String
let lastName: String
}
Ici, je suppose que 2 utilisateurs avec le même
id
ne peuvent pas exister
let users: [User] = ...
let dict = users.reduce([String:User]()) { (result, user) -> [String:User] in
var result = result
result[user.id] = user
return result
}
Maintenant dict
est un dictionnaire où le key
est le user id
et le value
est le user value
.
Pour accéder à un utilisateur via id
, vous pouvez maintenant écrire simplement
let user = dict["123"]
Étant donné un tableau d'un type donné Element
et une fermeture qui détermine le key
d'un Element
, la fonction générique suivante générera un Dictionary
de type [Key:Element]
func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
return elms.reduce([Key:Element]()) { (dict, Elm) -> [Key:Element] in
var dict = dict
dict[extractKey(Elm)] = Elm
return dict
}
}
Exemple
let users: [User] = [
User(id: "a0", firstName: "a1", lastName: "a2"),
User(id: "b0", firstName: "b1", lastName: "b2"),
User(id: "c0", firstName: "c1", lastName: "c2")
]
let dict = createIndex(elms: users) { $0.id }
// ["b0": {id "b0", firstName "b1", lastName "b2"}, "c0": {id "c0", firstName "c1", lastName "c2"}, "a0": {id "a0", firstName "a1", lastName "a2"}]
Comme indiqué par Martin R, la réduction créera un nouveau dictionnaire pour chaque itération de la fermeture associée. Cela pourrait entraîner une consommation de mémoire énorme.
Voici une autre version de la fonction createIndex
dans laquelle l'espace requis est O(n)) où n est la longueur de elms.
func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
var dict = [Key:Element]()
for Elm in elms {
dict[extractKey(Elm)] = Elm
}
return dict
}
Cette extension fonctionne pour toutes les séquences (y compris les tableaux) et vous permet de sélectionner les deux clé et valeur:
extension Sequence {
public func toDictionary<K: Hashable, V>(_ selector: (Iterator.Element) throws -> (K, V)?) rethrows -> [K: V] {
var dict = [K: V]()
for element in self {
if let (key, value) = try selector(element) {
dict[key] = value
}
}
return dict
}
}
Exemple:
let nameLookup = persons.toDictionary{($0.name, $0)}
Ce qui suit convertit un tableau en dictionnaire.
let firstArray = [2,3,4,5,5]
let dict = Dictionary(firstArray.map { ($0, 1) } , uniquingKeysWith: +)
Faites-le simplement,
let items = URLComponents(string: "https://im.qq.com?q=13&id=23")!.queryItems!
var dic = [String: Any?]()
items.foreach {
dic[$0.name] = $0.value
}
reduce
n'est pas très approprié,
let dic: [String: Any?] = items.reduce([:]) { (result: [String: Any?], item: URLQueryItem) -> [String: Any?] in
var r = result
r[item.name] = item.value // will create an copy of result!!!!!!
return r
}
Si je comprends bien votre question, vous voudriez convertir en Array
en Dictionary
.
Dans mon cas, je crée extension
pour le Array
et les clés du dictionnaire seront des index du Array
.
Exemple:
var intArray = [2, 3, 5, 3, 2, 1]
extension Array where Element: Any {
var toDictionary: [Int:Element] {
var dictionary: [Int:Element] = [:]
for (index, element) in enumerate() {
dictionary[index] = element
}
return dictionary
}
}
let dic = intArray.toDictionary
Si vous voulez suivre le modèle défini par la carte et réduire en Swift vous pouvez faire quelque chose de joli et fonctionnel comme ceci:
extension Array {
func keyBy<Key: Hashable>(_ keyFor: (Element) -> Key) -> [Key: Element] {
var ret = [Key: Element]()
for item in self{
ret[keyFor(item)] = item
}
return ret
}
}
Usage:
struct Dog {
let id: Int
}
let dogs = [Dog(id: 1), Dog(id: 2), Dog(id: 3), Dog(id: 4)]
let dogsById = dogs.keyBy({ $0.id })
// [4: Dog(id: 4), 1: Dog(id: 1), 3: Dog(id: 3), 2: Dog(id: 2)]