2014-07-02 12 views
24

Jak mogę łatwo dodawać elementy do tablicy w słowniku? To zawsze skarży się could not find member 'append' lub could not find an overload for '+='swift: modyfikowanie tablic w słownikach

var dict = Dictionary<String, Array<Int>>() 
dict["key"] = [1, 2, 3] 

// all of these fail 
dict["key"] += 4 
dict["key"].append(4) // xcode suggests dict["key"].?.append(4) which also fails 
dict["key"]!.append(4) 
dict["key"]?.append(4) 

// however, I can do this: 
var arr = dict["key"]! 
arr.append(4) // this alone doesn't affect dict because it's a value type (and was copied) 
dict["key"] = arr 

gdybym tylko przypisać tablicę do var, modyfikować go, a następnie przypisać je do dict, nie będę kopiując wszystko? to nie byłoby wydajne ani eleganckie.

Odpowiedz

29

Swift w wersji beta 5 dodał tę funkcję, a przybrałeś nową metodę w kilku próbach. Operatory rozpakowywania ! i ? przechodzą obecnie przez tę wartość do operatorów lub wywołań metod. To znaczy, można dodać do tej tablicy w jeden z następujących sposobów:

dict["key"]! += [4] 
dict["key"]!.append(4) 
dict["key"]?.append(4) 

Jak zawsze, należy uważać, o których operator użyć - życie rozpakowaniu wartość, która nie ma w swoim słowniku dadzą ci runtime error:

dict["no-key"]! += [5]  // CRASH! 

Zważywszy użyciu opcjonalnego łańcuchowym zawiedzie cicho:

dict["no-key"]?.append(5)  // Did it work? Swift won't tell you... 

Idealnie byłbyś w stanie korzystać z nowego operatora zerowy koalescencyjny ?? rozwiązać ten drugi przypadku, ale teraz to nie działa.


Odpowiedź z pre-beta Swift 5:

Jest to dziwactwo Swift, że nie jest możliwe, aby zrobić to, co próbujesz zrobić. Problem polega na tym, że wartość dowolnej zmiennej Opcjonalnej jest w rzeczywistości stałą - nawet podczas wymuszania rozpakowywania. Jeśli po prostu zdefiniować opcjonalnego tablicę, oto co możemy, a czego nie może zrobić:

var arr: Array<Int>? = [1, 2, 3] 
arr[0] = 5 
// doesn't work: you can't subscript an optional variable 
arr![0] = 5 
// doesn't work: constant arrays don't allow changing contents 
arr += 4 
// doesn't work: you can't append to an optional variable 
arr! += 4 
arr!.append(4) 
// these don't work: constant arrays can't have their length changed 

Powodem masz problemy ze słownika jest to, że indeksowanie słownika zwraca wartość opcjonalna, ponieważ nie ma gwarancji, że słownik będzie miał ten klucz. Dlatego tablicą w słowniku ma takie samo zachowanie jak opcjonalna tablicy powyżej:

var dict = Dictionary<String, Array<Int>>() 
dict["key"] = [1, 2, 3] 
dict["key"][0] = 5   // doesn't work 
dict["key"]![0] = 5  // doesn't work 
dict["key"] += 4   // uh uh 
dict["key"]! += 4   // still no 
dict["key"]!.append(4)  // nope 

Jeśli trzeba zmienić coś w tablicy w słowniku trzeba uzyskać kopię tablicy, zmiany Opisz i przypisanie, tak:

if var arr = dict["key"] { 
    arr.append(4) 
    dict["key"] = arr 
} 

ETA: sama technika działa w Swift beta 3, chociaż stałe tablice nie pozwalają już zmian w treści.

+0

Gdyby nie 'dict [ "klucz"] !. dołączania (4)' pracę wtedy? –

+1

Nie - subskrybowanie słownika przywraca Opcjonalnie, więc w pierwszym przykładzie pracujesz z opcjonalną tablicą. –

+0

Nie wiem, dlaczego Apple nie zdefiniował operatora + dla tablic. Jeśli zrobisz to sam, możesz użyć dict ["key"] = dict ["key"]! + 4. – Jessy

6

Jako prosty obejście można użyć NSMutableArray:

import Foundation 

var dict = Dictionary<String, NSMutableArray>() 
dict["key"] = [1, 2, 3] as NSMutableArray 
dict["key"]!.addObject(4) 

Używam skutecznie Takie proste rozwiązanie w moim projekcie:

https://github.com/gui-dos/Guigna/blob/5c02f7e70c8ee3b2265f6916c6cbbe5cd3963fb5/Guigna-Swift/Guigna/GuignaAppDelegate.swift#L1150-L1157

+3

To prawda, ponieważ 'NSMutableArray' jest klasą, w przeciwieństwie do macierzystych macierzy Swiftu, które są zdefiniowane jako structs. W tej implementacji 'dict [" key "]!' Jest wciąż niezmienne, ale to nie ma znaczenia, ponieważ możesz zmienić niezmienne właściwości ukrytej klasy. –

+3

To działa i jest bardzo prostym i rozsądnym rozwiązaniem. Jednak już zacząłem marzyć o kodzie NS * :( – figha

1

Oto co mówił Nate Cook, w komentarzach do jego jakościowej odpowiedzi.To jest to, co uważam za „łatwe [dodawanie] elementów do tablicy wewnątrz słownika”:

dict["key"] = dict["key"]! + 4 
dict["key"] = dict["key"] ? dict["key"]! + 4 : [4] 

Na razie musimy zdefiniować operatora + siebie.

@infix func +<T>(array: T[], element: T) -> T[] { 
    var copy = array 
    copy += element 
    return copy 
} 

Myślę, że ta wersja usuwa zbyt dużo bezpieczeństwa; może zdefiniować to za pomocą operatora złożonego?

@infix func +<T>(array: T[]?, element: T) -> T[] { 
    return array ? array! + element : [element] 
} 

dict["key"] = dict["key"] + 4 

Jest to najczystsze z możliwych, ale jestem zdezorientowany tym, jak działają wartości w tym przykładzie.

@assignment func +=<T>(inout array: T[]?, element: T) { 
    array = array + element 
} 

dict["key"] += 5 
+1

To, co jest warte, jest niesamowitym rozwiązaniem. Cóż, dla kogoś takiego jak ja, który wciąż jest daleki od zdobycia choćby odrobiny szybkości. Nadal trochę martwię się tym, że kopiuje rzeczy w tle, a ja zapominam o tym i używam tego w ciasnej pętli, w której wydajność jest bardzo ważna ... – figha

0

Użyj "dla", aby uzyskać wartości z wewnętrznej tablicy słownika. Oto przykład, aby pomóc Ci

var a = ["x":["a","b","c"], "y":["d"]] 

for b in a { 
    print(b) 
} 

wyjściowa:

("x", ["d"]) 
("y", ["a", "b", "c"])