2016-01-21 19 views
5

Czy istnieje odpowiednik dla natywnego języka Swift Dictionary na [NSDictionary initWithObjects: forKeys:]?Swift jest odpowiednikiem `[NSDictionary initWithObjects: forKeys:]`

Powiedz, że mam dwie tablice z kluczami i wartościami i chcę umieścić je w słowniku. W Objective-C, zrobiłbym to tak:

NSArray *keys = @[@"one", @"two", @"three"]; 
NSArray *values = @[@1, @2, @3]; 
NSDictionary *dict = [[NSDictionary alloc] initWithObjects: values forKeys: keys]; 

Oczywiście mogę iteracyjne z licznikiem przez obu tablicach, użyj var dict: [String:Int] i dodać rzeczy krok po kroku. Ale to nie wydaje się być dobrym rozwiązaniem. Korzystanie z zip i jest prawdopodobnie lepszym sposobem na powtórzenie obu w tym samym czasie. Jednak to podejście oznacza posiadanie zmiennego słownika, a nie niezmiennego.

let keys = ["one", "two", "three"] 
let values = [1, 2, 3] 
// ??? 
let dict: [String:Int] = ["one":1, "two":2, "three":3] // expected result 

Odpowiedz

3

one-liner, używając zip i reduce:

let dict = zip(keys, values).reduce([String:Int]()){ var d = $0; d[$1.0] = $1.1; return d } 

można skrócić wyraz reduce poprzez zdefiniowanie operatora + dla Dictionary i 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: +) 
+0

Definiowanie '+' jako operatora do użycia nie ma większego znaczenia. Każda inna nazwa funkcji byłaby bardziej oczywista. – orkoden

+1

Z jednej strony '+' ma znaczenie semantyczne, ponieważ dodaje nowy element do słownika, z drugiej strony może również zastąpić zamiast dodawać, więc jego efekt może nie zawsze być kumulowany. Osobiście uważam, że jest wystarczająco znaczący; może to być tylko ja, choć :) – Cristik

4

można po prostu użyć Swift równowartość initWithObjects:forKeys:

let keys = ["one", "two", "three"] 
let values = [1, 2, 3] 
var dict = NSDictionary.init(objects: values, forKeys: keys) 
+2

Zauważ, że przy takim podejściu twoje 'dict' będzie typu' NSDictionary', a nie '[String: Int]'. Zrobiłbyś siłę downcastu do '[String: Int]' w ten sposób: 'NSDictionary.init (objects: values, forKeys: keys) as![String: Int] ' – JAL

+1

Jestem świadomy, że używam tego, ale działa on tylko dla kluczy zgodnych z obiektami' NSCopying' i 'AnyObject'. Swiftowe słowniki działają również w przypadku typów pierwotnych, takich jak 'Int' w powyższym przykładzie lub' enums', 'structs' itp. Nie chcę używać' NSDictionary', ale natywnego 'Dictionary' Swifta – orkoden

+0

W tym przypadku 'Int' są przekonwertowane na 'NSNumber' automatycznie, jak możesz przetestować z' print (dict.allValues.first? .dynamicType) ' – orkoden

3

Praca czystego Swift rozwiązanie elemencie. Użyj zip do iteracji przez dwie tablice jako krotkę, a następnie utwórz słownik dla każdego klucza, wartość w krotce.

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))]" 

Można też użyć forEach na 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" 
+0

To również było moje rozwiązanie, powinienem był dodać to do mojego pytania. Wadą jest to, że w tym przypadku kończy się zmiennym słownikiem. – orkoden

+1

@orkoden Ah Widzę. Dodałem rozwiązanie, które używa 'map', która jest trochę mniej gadatliwa. Mam nadzieję, że to działa dla twoich potrzeb. Nadal myślę o tym, jak stworzyć dla ciebie niezmienny słownik. – JAL

+2

Nie potrzebujesz nawet tablicy "Array (Zip2Sequence (keys, values))" używając 'for (key, value) in zip (keys, values)' is enough. – orkoden

1
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] 

jaka jest różnica, czy to jest w porównaniu do rozwiązania zip? trudno powiedzieć ... dla mnie rodzaj zip adnotacja jest największym problemem

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] 

ale wyliczyć rozwiązaniem jest też trochę szybciej

1

Od Swift 4 można utworzyć bezpośrednio ze słownika sekwencja par kluczy/wartości:

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] 

zakłada się, że wszystkie klucze są różne, w przeciwnym przypadku przerwania z wyjątkiem wykonawczego.

Jeśli klawisze nie są gwarancją odrębny wtedy można zrobić

let keys = ["one", "two", "one"] 
let values = [1, 2, 3] 

let dict = Dictionary(zip(keys, values), uniquingKeysWith: { $1 }) 

print(dict) // ["one": 3, "two": 2] 

Drugi argument jest zamknięcie, które określa, która wartość „wygrywa” w przypadku duplikatów kluczy.