Mutating nested arrays in Swift Dictionary through custom accessor method - swift

Say that we have a dictionary of arrays:
var dict: [Int: [Int]] = [:]
Is there something special about Dictionary's subscript methods? Can somebody explain why the following in-place append works:
dict[1] = []
dict[1]?.append(200)
// dict is now [1: [200]]
but the following doesn't:
var xs = dict[1]
xs?.append(300)
// dict is still [1: [200]], not [1: [200, 300]]
I (kind of) understand why the latter doesn't update the original dictionary, as it creates a copy of the array. But I don't understand why the first one works, I would assume that it similarly creates a copy.
More over (this is the actual problem I have), can I implement a method that allows similar in-place update behavior? The following code doesn't work:
extension Dictionary {
func mget(key: Key) -> Value? {
return self[key]
}
}
dict.mget(1)?.append(400)
It produces the following error:
49> d.mget(1)?.append(400)
repl.swift:49:12: error: immutable value of type '[Int]' only
has mutating members named 'append'
d.mget(1)?.append(400)
^ ~~~~~~

Related

How can make a custom extension for Dictionary in Swift?

I am trying to add a function to Dictionary, this func called add, which has 2 input values (key and value). I tried to make the func be generic, that means I want key and value be able take any Type. I end it up to this down non-working code. What I should do next to make this func work?
extension Dictionary {
func add<Key, Value>(key: Key, value: Value) {
self[Key] = Value
}
}
First, you are trying to assign a type instead of an instance to the dictionary.
Type of expression is ambiguous without more context):
Second, you need to declare your method as mutating.
Cannot assign through subscript: subscript is get-only).
Third, you are creating two new generic types that have no relation with the Dictionary generic Key and Value types.
Cannot convert value of type 'Key' (generic parameter of instance method 'add(key:value:)') to expected argument type 'Key' (generic parameter of generic struct 'Dictionary').
extension Dictionary {
mutating func add(key: Key, value: Value) {
self[key] = value
}
}
var dict: [String: Int] = [:]
dict.add(key: "One", value: 1)
dict // ["One": 1]

Using .sortInPlace within a .forEach closure Swift

I am attempting to sort the arrays within a dictionary, but am getting an error. Here is the code I have tried. What am I missing & why won't this compile?
var dict = [Int: [String]]()
dict[1] = ["Zack", "James", "Bill", "Quin", "Mike", "Adam"]
dict[1]?.sortInPlace()
dict.forEach{$0.1.sortInPlace()} // error: cannot use mutating member on immutable value of type '[String]'
Edit: I was able to get the following code to work after realizing that the for each loop assigns a constant by default:
db.forEach{db[$0.0] = $0.1.sort()}
Swift, by default assigns each value inside closure to be immutable. You can modify the default behavior by declaring the variable as mutable using var as this,
dict.forEach({ (key: Int, var value: [String]) in
value.sortInPlace()
print(value)
})

"Deep-copy" of Swift dictionary with map()?

I have a GKGameModel that stores its internal state in an array a of Cards and a dictionary b that maps from Ints to arrays of Cards. GameplayKit mandates that I must copy this internal state in setGameModel:.
The following code is meant to just-copy the array and "deep-copy" the dictionary. FWIK this should be sufficient since Cards themselves never change.
var a: [Card]
var b: [Int: [Card]]
func setGameModel(gameModel: GKGameModel) {
let otherGameModel = gameModel as! GameModel
a = otherGameModel.a
b = otherGameModel.b.map { (i: Int, cards: [Card]) in (i, cards) }
}
However, this causes the following syntax error in the line that attempt the "deep-copy":
Cannot assign a value of type '[(Int, [Card])]' to a value of type
'[Int, [Card]]'.
What am I doing wrong?
In your case:
b = otherGameModel.b
is sufficient.
Because, Array and Dictionary are both value types. So when it is assigned to another variable, it will be deep copied.
var bOrig: [Int: [Int]] = [1: [1,2,3], 2:[2,3,4]]
var bCopy = bOrig
bCopy[1]![2] = 30
bOrig[1]![2] // -> 3
bCopy[1]![2] // -> 30
The error message reveals there is a type mismatch:
variable b is declared as Dictionary<Int,[Card]> but the map function returns an Array of tuplets (Int, [Card])

Strange compiler error with Dictionary<String, String>

I'm not sure if I'm being an idiot or there is a compiler bug in the current Swift compiler but...
I declare a function into which I pass a Dictionary<String, String>, in that method I attempt to add an item into the Dictionary, the method is
func writeStatus(res: Dictionary<String, String>) {
res["key"] = "value"
}
and I get the compiler error
'#lvalue $T5 is not identical to '(String, String)'
In the method the Dictionary is declared in i can add items fine. So my question is how to I add items into a dictionary in a function? I could do all this in the line function but I hate functions that do too much.
I am using the Swift compiler in Xcode 6.2 beta if that's any help.
Thanks
Not sure if this is more helpful, but if you want the dictionary to be mutated, it might be an idea to use an inout argument:
func addDict(inout res: [String: String]) {
res["key"] = "value"
}
var res = [String: String]()
addDict(&res)
println(res) // [key: value]
This doesn't have any errors, and res will have the values assigned to it in the addDict function.
By default all function parameters are immutable. So if you try to mutate them you will get compiler error. If you want to mutate function parameter define it as var.
func writeStatus(var res: [String: String]) {
res[key] = "value"
}
But here it will not work. Because Dictionary is value type and value types are copied when they are passed into a function. And your mutation will only have affect on copied value. So in this senario defining parameter as inout will solve your problem

Can you append to an array inside a dict?

In Swift, you can declare a dict where the value is an array type, eg:
var dict: [Int: [Int]] = [:]
However, if you assign an array for a given key:
dict[1] = []
then it appears that Swift treats the array as immutable. For example, if we try:
(dict[1] as [Int]).append(0) // explicit cast to avoid DictionaryIndex
then we get the error 'immutable value of type [Int] has only mutating members named 'append''.
If we explicitly make the array mutable, then the append works, but doesn't modify the original array:
var arr = dict[1]!
arr.append(0) // OK, but dict[1] is unmodified
How can you append to an array which is a dict value?
More generally, how can you treat the values of a dictionary as mutable?
One workaround is to reassign the value afterwards, but this does not seem like good practice at all:
var arr = dict[1]!
arr.append(0)
dict[1] = arr
Try unwrapping the array instead of casting:
dict[1]!.append(0)