Strange compiler error with Dictionary<String, String> - swift

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

Related

Swift: mutate cast parameter (Error: Cannot use mutating member on immutable value of type 'MyObjectType' (aka 'Dictionary<Int, Int>')

I have a functions where I passed in Dictionary [Int : Int] as inout T and would need to write into the Dictionary. This function is included as a function of a protocol.
However, after casting T into MyObjectType (Dictionary), it becomes immutable.
Question: How can I make a generic parameter (it can be passed in as struct, class, array or dictionary) mutable after casting?
typealias MyObjectType = [Int : Int] // Key is a enum with Int as RawValue
static func myGenericFunction<T>(_ object: inout T) {
(object as! MyObjectType).updateValue(0, forKey: 1)
}
I have tried casting it to NSMutableDictionary as below which compile successfully but crash when running with simulator.
(object as! NSMutableDictionary)[Int(pMetric.selectedUnit)] = Int(pMetric.metricType)
Thanks for any good solution!

Generic dictionary extension error - ambiguous reference to subscript

I'm playing with generics in Swift 3 (Xcode 8.2.1) and I don't understand why this won't compile. I also tried self.updateValue... and that fails also.
extension Dictionary {
mutating func mergeWith<K: Hashable, V: AnyObject> (a: [K:V]) -> [K:V] {
for (k,v) in a {
self[k] = v // compile error: Ambiguous reference to member 'subscript'
}
}
}
I'm trying to limit the types of generics K and V to what works with a Dictionary, but that doesn't seem to work?
It's not a particularly helpful error, but the problem is that you're introducing new local generic placeholders K and V in your method – which need not be related in any way to the Dictionary's Key and Value types (remember that generic placeholders are satisfied by the caller, not the callee).
So just simply remove them and use the existing generic placeholders Key and Value instead, i.e take a [Key : Value] parameter. Or better still, take advantage of the fact that Swift automatically infers the generic placeholders of a generic type when you refer to it inside of itself, and just type the parameter as Dictionary (which will resolve to Dictionary<Key, Value>).
extension Dictionary {
mutating func merge(with dict: Dictionary) {
for (key, value) in dict {
self[key] = value
}
}
}
Also mutating methods usually don't return the mutated instance, so I removed the return type from your method.

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)
})

Mutating nested arrays in Swift Dictionary through custom accessor method

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)
^ ~~~~~~

Any? to Dictionary Cast and retrieving value

a function test receiving Any? optional object, once we are sure it's not nil, how can we get hold of underlying dictionary value.
Error: Any doesn't have a member named subscript. Casting to a Dictionary didn't help either. println(paramValue as Dictionary) Any is not convertible to Dictionary<Key, Value>
func test(params: Any?) {
if let paramValue = params {
println(paramValue)
//println(paramValue["value"])
}
}
test(["value": 10])
println(paramValue as Dictionary) won't work because dictionary is type-safe.
You could use:
println(paramValue as [String : Int])
if those will always be the right types.
If they might change, use optional casting (as? instead of as).