ios - Rápido equivalente a `[NSDictionary initWithObjects: forKeys:]`
objective-c swift (5)
¿Existe un equivalente para el
Dictionary
nativo de Swift a
[NSDictionary initWithObjects: forKeys:]
?
Digamos que tengo dos matrices con claves y valores y quiero ponerlas en un diccionario. En Objective-C lo haría así:
NSArray *keys = @[@"one", @"two", @"three"];
NSArray *values = @[@1, @2, @3];
NSDictionary *dict = [[NSDictionary alloc] initWithObjects: values forKeys: keys];
Por supuesto, puedo iterar con un contador a través de ambas matrices, usar un
var dict: [String:Int]
y agregar cosas paso a paso.
Pero eso no parece ser una buena solución.
Usar
zip
y
enumerate
son probablemente mejores formas de iterar sobre ambos al mismo tiempo.
Sin embargo, este enfoque significa tener un diccionario mutable, no uno inmutable.
let keys = ["one", "two", "three"]
let values = [1, 2, 3]
// ???
let dict: [String:Int] = ["one":1, "two":2, "three":3] // expected result
A partir de Swift 4 , puede crear un diccionario directamente a partir de una secuencia de pares clave / valor:
let keys = ["one", "two", "three"]
let values = [1, 2, 3]
let dict = Dictionary(uniqueKeysWithValues: zip(keys, values))
print(dict) // ["one": 1, "three": 3, "two": 2]
Esto supone que todas las claves son diferentes, de lo contrario se cancelará con una excepción de tiempo de ejecución.
Si no se garantiza que las claves sean distintas, puede hacerlo
let keys = ["one", "two", "one"]
let values = [1, 2, 3]
let dict = Dictionary(zip(keys, values), uniquingKeysWith: { $1 })
print(dict) // ["one": 3, "two": 2]
El segundo argumento es un cierre que determina qué valor "gana" en el caso de claves duplicadas.
Simplemente puede usar el equivalente Swift de
initWithObjects:forKeys:
let keys = ["one", "two", "three"]
let values = [1, 2, 3]
var dict = NSDictionary.init(objects: values, forKeys: keys)
Trabajando pura solución Swift con structs.
Use
zip
para recorrer sus dos matrices como una tupla, y luego cree un diccionario para cada clave, valor en la tupla.
struct SomeStruct {
var someVal: Int?
}
var keys = [String]()
var values = [SomeStruct]()
for index in 0...5 {
keys.append(String(index))
values.append(SomeStruct(someVal: index))
}
var dict = [String : Any]()
for (key, value) in zip(keys, values) {
dict[key] = value
}
print(dict) // "["4": SomeStruct(someVal: Optional(4)), "2": SomeStruct(someVal: Optional(2)), "1": SomeStruct(someVal: Optional(1)), "5": SomeStruct(someVal: Optional(5)), "0": SomeStruct(someVal: Optional(0)), "3": SomeStruct(someVal: Optional(3))]"
También
forEach
usar
forEach
en
zip
:
var dict = [String : Any]()
zip(keys, values).forEach { dict[$0.0] = $0.1 }
print(dict) // "["4": SomeStruct(someVal: Optional(4)), "2": SomeStruct(someVal: Optional(2)), "1": SomeStruct(someVal: Optional(1)), "5": SomeStruct(someVal: Optional(5)), "0": SomeStruct(someVal: Optional(0)), "3": SomeStruct(someVal: Optional(3))]/n"
Una línea, usando
zip
y
reduce
:
let dict = zip(keys, values).reduce([String:Int]()){ var d = $0; d[$1.0] = $1.1; return d }
Puede acortar la expresión de
reduce
definiendo el operador
+
para un
Dictionary
y una
tuple
:
func +<K,V>(lhs: [K:V], rhs: (K, V)) -> [K:V] {
var result = lhs
result[rhs.0] = rhs.1
return result
}
let dict = zip(keys, values).reduce([String:Int](), combine: +)
let keys = ["one", "two", "three"]
let values = [1, 2, 3]
func createDict<K:Hashable,V>(keys: [K], values:[V])->[K:V] {
var dict: [K:V] = [:]
// add validity checks here by yourself !
// and return early, or throw an error ...
keys.enumerate().forEach { (index,element) -> () in
dict[element] = values[index]
}
return dict
}
let dict = createDict(keys, values: values)
// ["one": 1, "three": 3, "two": 2]
let dict2:[Int:Any] = createDict([1,2,3,4,5], values: [true,"two",3.4,5,[1,2,3]])
// [5: [1, 2, 3], 2: "two", 3: 3.4, 1: true, 4: 5]
¿Cuál es la diferencia si se compara con la solución zip? difícil de decir ... para mí, la anotación de tipo zip es el mayor problema
let a:Zip2Sequence<[Int],[Any]> = zip([1,2,3,4,5], [true,"two",3.4,5,[1,2,3]])
var d:[Int:Any] = [:]
a.forEach { (key, value) -> () in
d[key] = value
}
print(d)
// [5: [1, 2, 3], 2: "two", 3: 3.4, 1: true, 4: 5]
pero enumerar la solución también es un poco más rápido