Je veux obtenir de ce tableau de chaînes
let entries = ["x=5", "y=7", "z=10"]
pour ça
let keyValuePairs = ["x" : "5", "y" : "7", "z" : "10"]
J'ai essayé d'utiliser map
mais le problème semble être qu'une paire clé-valeur dans un dictionnaire n'est pas un type distinct, c'est juste dans mon esprit, mais pas dans le type Dictionnaire donc je ne pouvais pas vraiment fournir une fonction de transformation car il y a n'y a rien à transformer. De plus, map
renvoie un tableau, donc rien ne va.
Des idées?
Swift 4
Comme mentionné par fl034, ceci peut être simplifié avec Swift 4 où une version vérifiée par erreur ressemble à ceci:
let foo = entries
.map { $0.components(separatedBy: "=") }
.reduce(into: [String:Int64]()) { dict, pair in
if pair.count == 2, let value = Int64(pair[1]) {
dict[pair[0]] = value
}
}
Encore plus simple si vous ne voulez pas les valeurs en Ints:
let foo = entries
.map { $0.components(separatedBy: "=") }
.reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
Ancien TL; DR
Moins vérification d'erreur, il ressemble à peu près à:
let foo = entries.map({ $0.componentsSeparatedByString("=") })
.reduce([String:Int]()) { acc, comps in
var ret = acc
ret[comps[0]] = Int(comps[1])
return ret
}
Utilisez map pour transformer le [String]
en un [[String]]
divisé, puis construisez le dictionnaire de [String:Int]
à partir de celui-ci en utilisant une réduction.
Ou, en ajoutant une extension à Dictionary
:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
(C'est une extension très utile, vous pouvez l'utiliser pour beaucoup d'opérations map/filter sur les dictionnaires, vraiment dommage qu'il n'existe pas par défaut)
Cela devient encore plus simple:
let dict = Dictionary(elements: entries
.map({ $0.componentsSeparatedByString("=") })
.map({ ($0[0], Int($0[1])!)})
)
Bien sûr, vous pouvez également combiner les deux appels de carte, mais je préfère diviser les transformations individuelles.
Si vous souhaitez ajouter une vérification d'erreur, flatMap
peut être utilisé à la place de map
:
let dict2 = [String:Int](elements: entries
.map({ $0.componentsSeparatedByString("=") })
.flatMap({
if $0.count == 2, let value = Int($0[1]) {
return ($0[0], value)
} else {
return nil
}})
)
De nouveau, si vous le souhaitez, vous pouvez évidemment fusionner la map
dans la flatMap
ou la scinder pour plus de simplicité.
let dict2 = [String:Int](elements: entries.flatMap {
let parts = $0.componentsSeparatedByString("=")
if parts.count == 2, let value = Int(parts[1]) {
return (parts[0], value)
} else {
return nil
}}
)
Une façon de le faire est en deux étapes avec map
et reduce
avec un tuple comme valeur intermédiaire, par exemple:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.map { (str) -> (String, String) in
let elements = str.characters.split("=").map(String.init)
return (elements[0], elements[1])
}.reduce([String:String]()) { (var dict, kvpair) in
dict[kvpair.0] = kvpair.1
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
les sorties:
Value for key 'y' is Optional("7").
Value for key 'x' is Optional("5").
Value for key 'z' is Optional("10").
ou avec une seule reduce
avec le même résultat:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.reduce([String:String]()) { (var dict, entry) in
let elements = entry.characters.split("=").map(String.init)
dict[elements[0]] = elements[1]
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
Utilisez la nouvelle méthode reduce(into: Result)
de Swift 4 :
let keyValuePairs = entries.reduce(into: [String:String]()) { (dict, entry) in
let key = String(entry.first!)
let value = String(entry.last!)
dict[entry.first!] = entry.last!
}
Bien sûr, la division de votre chaîne pourrait être améliorée.
Bonnes réponses déjà. Voici un moyen supplémentaire avec une extension de type collection. Vous pouvez convertir n'importe quel type de collection en dictionnaire, tableau ou ensemble.
extension CollectionType {
func map2Dict<K, V>(@noescape map: ((Self.Generator.Element) -> (K, V)?)) -> [K: V] {
var d = [K: V]()
for e in self {
if let kV = map(e) {
d[kV.0] = kV.1
}
}
return d
}
func map2Array<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> [T] {
var a = [T]()
for e in self {
if let o = map(e) {
a.append(o)
}
}
return a
}
func map2Set<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> Set<T> {
return Set(map2Array(map))
}
}
Voici l'exemple d'utilisation.
let entries = ["x=5", "y=7", "z=10"]
let dict:[String: String] = entries.map2Dict({
let components = $0.componentsSeparatedByString("=")
return (components.first!, components.last!)
})
print("dict \(dict)")
J'aime @paulgriffiths répondre, mais si vous avez une aversion extrême pour les erreurs d'exécution, vous pouvez aller encore plus loin pour vous assurer que chaque chaîne du tableau initial comporte réellement les deux éléments requis ...
La différence importante entre ce code et les autres est que je vérifie qu'il existe bien un "=" dans la chaîne avec des éléments des deux côtés. La flatMap
filtre efficacement ceux qui échouent.
extension Dictionary {
func withUpdate(key: Key, value: Value) -> Dictionary<Key, Value> {
var result = self
result[key] = value
return result
}
}
let entries = ["x=5", "y=7", "z=10"]
let keyValues = entries.flatMap { str -> (String, String)? in
let split = str.characters.split("=").map(String.init)
return split.count > 1 ? (split[0], split[1]) : nil
}
let keyValuePairs = keyValues.reduce([String: String]()) { $0.withUpdate($1.0, value: $1.1) }
Manière simple:
let array = ["key1", "key2", "key3"]
let dict = array.flatMap({["\($0)" : "value"]})
Sortie:
[(clé: "clé1", valeur: "valeur"), (clé: "clé2", valeur: "valeur"), (clé: "clé3", valeur: "valeur")]
Manière un peu plus complexe:
let array = ["key1", "key2", "key3"]
let dict = array.enumerated().flatMap({["\($0.element)" : "value\($0.offset)"]})
Sortie:
[(clé: "clé1", valeur: "valeur0"), (clé: "clé2", valeur: "valeur1"), (clé: "clé3", valeur: "valeur2")]
Et pour tous ceux qui aiment la surcharge et les one-liners.
public func +<K, V>(lhs: [K:V], rhs: [K:V]) -> [K:V] {
var lhs: [K:V] = lhs
for (key, value) in rhs {
lhs[key] = value
}
return lhs
}
let array = ["x=5", "y=7", "z=10"]
let dictionary = array.map({ $0.componentsSeparatedByString("=") }).reduce([:]) { $0 + [$1[0]: $1[1]] }
print(dictionary) // ["y": "7", "x": "5", "z": "10"]
Si vous avez deux tableaux parallèles, vous pouvez utiliser Zip()
, Array()
et l'extension Dictionary de David Berry :
let numbers = [10, 9, 8]
let strings = ["one", "two", "three", "four"]
let dictionary = Dictionary(elements: Array(Zip(numbers, strings)))
// == [10: "one", 9: "two", 8: "three"]
N'oubliez pas d'ajouter l'extension:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}