Подтвердить что ты не робот

Swift: изменение массивов внутри словарей

Как я могу легко добавлять элементы в массив внутри словаря? Он всегда жалуется на could not find member 'append' или could not find an overload for '+='

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 a value type (and was copied)
dict["key"] = arr

Если я просто назначу массив var, измените его, а затем переназначить его в dict, не буду ли я копировать все? это не было бы эффективным и элегантным.

4b9b3361

Ответ 1

Swift beta 5 добавил эту функциональность, и вы закрепили новый метод за пару своих попыток. Операторы разворота ! и ? теперь проходят через значение для операторов или вызовов методов. То есть вы можете добавить к этому массиву любым из следующих способов:

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

Как всегда, будьте осторожны с тем, какой оператор вы используете - принудительное разворачивание значения, которое не находится в вашем словаре, даст вам ошибку времени выполнения:

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

В то время как использование необязательной цепочки не будет работать тихо:

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

В идеале вы сможете использовать новый оператор коалесцирования нуль ?? для решения этого второго случая, но сейчас, когда он не работает.


Ответ от pre-Swift beta 5:

Это причуда Свифта, что невозможно сделать то, что вы пытаетесь сделать. Проблема в том, что значение любой необязательной переменной на самом деле является константой - даже при принудительной разворачивании. Если мы просто определим необязательный массив, то что мы можем и не можем сделать:

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

Причина, по которой у вас возникают проблемы со словарем, заключается в том, что подписка на словарь возвращает значение "Необязательно", поскольку нет гарантии, что словарь будет иметь этот ключ. Следовательно, массив в словаре имеет то же поведение, что и дополнительный массив, выше:

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

Если вам нужно что-то изменить в массиве в словаре, вам нужно будет получить копию массива, изменить его и переназначить следующим образом:

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

ETA: тот же метод работает в бета-версии Swift 3, хотя постоянные массивы больше не позволяют изменять содержимое.

Ответ 2

В качестве простого обходного пути вы можете использовать NSMutableArray:

import Foundation

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

Я эффективно использую такое простое решение в своем проекте:

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

Ответ 3

Вот что я говорил Нейту Куку в комментариях к его качественному ответу. Это то, что я считаю "легко [добавлением] элементов в массив внутри словаря":

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

Теперь нам нужно определить сам оператор +.

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

Я думаю, что эта версия устраняет слишком большую безопасность; может определить его с помощью сложного оператора?

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

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

Наконец, это самый чистый, который я могу получить, но я смущен тем, как в этом примере работают значения/ссылки массива.

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

dict["key"] += 5

Ответ 4

Принятый ответ обходит следующую гораздо более простую возможность, которая также работает для более старых версий Swift:

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

print(dict)

dict["key", default: [Int]()].append(4)

print(dict)

Это напечатает:

["key": [1, 2, 3]]
["key": [1, 2, 3, 4]]

И это:

var dict = Dictionary<String, Array<Int>>()
dict["key", default: [Int]()].append(4)
print(dict)

напечатает:

["key": [4]]

Ответ 5

Используйте 'for in' для получения значений из внутреннего словаря. Вот пример, который поможет вам

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

for b in a {
    print(b)
}

Вывод:

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