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, не буду ли я копировать все? это не было бы эффективным и элегантным.
Ответы
Ответ 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"])