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).
Related
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]
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!
Using Xcode 10, but did not migrate to Swift 4.2, so my project is still running with Swift 4.1.
Lets assume i have the following extension on Dictionary:
extension Dictionary where Key: ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
I use this function to access values in a hashmap in a type safe manner like:
let dict: [String: Any] = ["foo": "bar"]
let foo: String? = dict.find(key: "foo") // prints "bar"
My problem surfaces, when i would like to have Any type returned from my find function, like:
let bar: Any? = dict.find(key: "bar")
Pre Xcode 10, this function used to return to me plain and simple nil if the key was not found in the hashmap.
However, post Xcode 10, it returns Optional.some(nil).
I understand, that Any types can be initialised like the following:
let foo: Any = Optional<String>.none
I guess in my case something similar happens. Does anybody has an idea, how to work around it and still return nil from the find function?
This is due to an intentional change (#13910) where the compiler is now more conservative with unwrapping an optional value that's being cast to a generic placeholder type. Now, the results you get are more consistent with those that you would get in a non-generic context (see SR-8704 for further discussion).
For example:
// note the constraint `where Key : ExpressibleByStringLiteral` is needlessly restrictive.
extension Dictionary where Key : ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
let dict: [String: Any] = ["foo": "bar"]
let genericBar: Any? = dict.find(key: "bar")
print(genericBar as Any) // in Swift 4.1: nil, in Swift 4.2: Optional(nil)
// `T` in the above example is inferred to be `Any`.
// Let's therefore substitute `as? T` with `as? Any`.
let nonGenericBar = dict["bar"] as? Any
print(nonGenericBar as Any) // in both versions: Optional(nil)
As you can see, you now get Optional(nil) regardless of whether a generic placeholder was used in order to perform the cast, making the behaviour more consistent.
The reason why you end up with Optional(nil) is because you're performing a conditionally casting an optional value. The conditional cast on its own results in an optional value in order to indicate success or failure, and putting the resulting optional value in the success case gives you a doubly wrapped optional. And because you're casting to Any, which can represent an Optional value, no unwrapping needs to be done in order to "fit" the value in the resultant type.
If you wish to flatten the resulting optional into a singly wrapped optional, you can either coalesce nil:
extension Dictionary {
func find<T>(key: Key) -> T? {
return (self[key] as? T?) ?? nil
}
}
Or unwrap the value before casting, for example using a guard:
extension Dictionary {
func find<T>(key: Key) -> T? {
guard let value = self[key] else { return nil }
return value as? T
}
}
or, my preferred approach, using flatMap(_:):
extension Dictionary {
func find<T>(key: Key) -> T? {
return self[key].flatMap { $0 as? T }
}
}
That all being said, I often find the usage of [String: Any] to be a code smell, strongly indicating that a stronger type should be used instead. Unless the keys can really be arbitrary strings (rather than a fixed set of statically known keys), and the values can really be of any type for a particular key – there's almost certainly a better type you can be using to model your data with.
I found an interesting function in the Swift Tests source code:
func test(_ v: A????, _ cast: (A????) -> B?)
Since Type? is just syntax sugar for Optional<Type>, this means that the type of the argument v can be rewritten as Optional<Optional<Optional<Optional<A>>>>.
I know that this function is used to test optional types, so it is definitely going overboard with the arguments v and cast, but what would be the actual use of having "an optional of an an optional, etc." type in Swift (if any)?
These occur sometimes in Swift when you are accessing data.
One example is if you have a dictionary with an optional type for the value, and then you look up a value in that dictionary:
let rockStars: [String: String?] = ["Sting": nil, "Elvis": "Presley", "Bono": nil, "Madonna": nil]
let lastName = rockStars["Elvis"]
print(lastName as Any)
Optional(Optional("Presley"))
This happens because a dictionary look up can fail when the key is not present, and it returns nil in that case. So the return type of a dictionary lookup has to be the value type wrapped in an optional. A look up from the rockStars dictionary returns a 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