Looping through dictionary in Swift 3 errror - dictionary

I have a dictionary that I declare like so:
var dictBarData = [String: Any]()
I then declare and populate a second dictionary like so, and then add the second dictionary to the first:
var dictThisBar = [String:Any]()
dictThisBar["category"] = category
dictThisBar["barValue"] = value
dictThisBar["barView"] = vThisBar
dictBarData[String(barIndex)] = dictThisBar
When I go to retrieve them I do this:
for (key, value) in dictBarData {
let dictThisBar = dictBarData[key]
for(key, value) in dictThisBar {
}
}
But I am getting an error on the second for loop, type Any does not conform to protocol sequence. Type Any does not conform to sequence protocol

According to the declaration all values in dictBarData are Any.
You have to cast the value down to
let dictThisBar = dictBarData[key] as! [String:Any]
However it's highly recommended to use optional bindings
if let dictThisBar = dictBarData[key] as? [String:Any] { ...

Related

How to force unwrap Optional String stored as Any

How do i force unwrap an optional value thats "stored" as Any?
let optionalString: String? = "optional string"
let anyString: Any = optionalString
if let unwrappedString = anyString as? String {
println(unwrappedString)
// does not recognize the anyString as an optinal String
}
How do i write an if statement that force unwraps the value stored in anyString and prints the optionalString value? I have to do it only accessing the anyString attribute.
The use case for this is to get values out of MirrorType, which stores the attribute values as Any.
Ignore answer as not really providing a solution - the problem looks like a compiler bug
I'm not deleting it by now so readers know what I tried, and how I misunderstood the real question:
How to unwrap an optional value stored in a variable of Any type
Also be sure to read comments.
In your code you are using optional binding. Forced unwrapping instead uses the postfix ! operator:
println(optionalString!)
but if the optional string contains a nil value, that throws a runtime exception. So unless you are 100% sure it's not nil, I recommend using optional binding:
if let unwrappedString = optionalString {
println(unwrappedString)
}
That said, if you have an optional string stored in an Any variable, using optional downcasting and optional binding you can extract as follows:
var anyString: Any? = "optional string" as String?
if let unwrappedString = anyString as? String {
println(unwrappedString)
}
If you want to use a forced downcast, that's simply:
println(anyString as String)
but as in forced unwrapping, this causes a runtime exception if anyString is nil.
If the anyString variable is not optional, things are even simpler, because there's no optional binding, just optional downcasting, although the related code looks exactly the same:
var anyString: Any = "optional string"
if let downcastedString = anyString as? String {
println(downcastedString)
}
If you have an Any then you can test if it's optional and it it's nil by using something as:
var anyValue: String? = "The string"
var theValue: Any = anyValue
let mi:MirrorType = reflect(theValue)
if mi.disposition == .Optional {
if mi.count == 0 { return NSNull() } // Optional.None
let (name,some) = mi[0]
theValue = some.value
}
Now theValue will just be a String and not an Optional String. So you can cast it and use it as a String.

Is it possible to have a dictionary with a mutable array as the value in Swift

I am trying to do this:
var dictArray = [String:[String]]()
dictArray["test"] = [String]()
dictArray["test"]! += "hello"
But I am getting the weird error NSString is not a subtype of 'DictionaryIndex<String, [(String)]>'.
I just want to be able to add objects to an array inside a dictionary.
Update: Looks like Apple considers this a "known issue" in Swift, implying it will work as expected eventually. From the Xcode 6 Beta 4 release notes:
...Similarly, you cannot modify the underlying value of a mutable
optional value, either conditionally or within a force-unwrap:
tableView.sortDescriptors! += NSSortDescriptor(key: "creditName", ascending: true)
Workaround: Test the optional value explicitly and then assign the
result back:
if let window = NSApplication.sharedApplication.mainWindow {
window.title = "Currently experiencing problems"
}
tableView.sortDescriptors = tableView.sortDescriptors!
You can only do this
var dictArray = [String:[String]]()
dictArray["test"] = [String]()
var arr = dictArray["test"]!;
arr += "hello"
dictArray["test"] = arr
because dictArray["test"] give you Optional<[String]> which is immutable
6> var test : [String]? = [String]()
test: [String]? = 0 values
7> test += "hello"
<REPL>:7:1: error: '[String]?' is not identical to 'UInt8'
append also won't work due to the same reason, Optional is immutable
3> dictArray["test"]!.append("hello")
<REPL>:3:18: error: '(String, [(String)])' does not have a member named 'append'
dictArray["test"]!.append("hello")
^ ~~~~~~
BTW the error message is horrible...
You may use NSMutableArray instead of [String] as a value type for your dictionary:
var dictArray: [String: NSMutableArray] = [:]
dictArray["test"] = NSMutableArray()
dictArray["test"]!.addObject("hello")
This is still an issue in Swift 3. At least I was able to create method that can handle it for you.
func appendOrCreate(newValue: Any, toArrayAt key: String, in existingDictionary: inout [AnyHashable:Any]) {
var mutableArray = [Any]()
if let array = existingDictionary[key] as? [Any]{
//include existing values in mutableArray before adding new value
for existingValue in array {
mutableArray.append(existingValue)
}
}
//append new value
mutableArray.append(newValue)
//save updated array in original dictionary
existingDictionary[key] = mutableArray
}
The problem is that we want class semantics here but have to use structs. If you put class objects into the dictionary, you get what you want!
So, if you haveĀ¹ to have mutable values, you can wrap them in a class and perform updates with a closure:
class MutableWrapper<T> {
var rawValue: T
init(_ val: T) {
self.rawValue = val
}
func update(_ with: (inout T) -> Void) {
with(&self.rawValue)
}
}
Example:
func foo() {
var dict = [String: MutableWrapper<[String]>]()
dict["bar"] = MutableWrapper(["rum"])
dict["bar"]?.update({$0.append("gin")})
print(dict["bar"]!.rawValue)
// > ["rum", "gin"]
}
For what it's worth, I do not see a way to keep caller and wrapper in sync. Even if we declare init(_ val: inout T) we will end up with a copy in rawValue.
Performance is not necessarily an issue since the compiler optimizes structs heavily. I'd benchmark any mutable solution against what looks like lots of copy-updates in the code.
Since Swift 4.1 you can provide a default value to the subscript which allows you to solve this quite naturally now:
dictArray["test", default: []].append("hello")

Downcast element when reading from Dictionary in Swift

I'm trying to access an element a dictionary element and downcast it to a type other than AnyObject but keep getting the same compiler error: Could not find an overload for 'subscript' that accepts the supplied arguments.
I know I can just do this using two if statements like so:
if let x = dict["key"] {
if let y = x as? String {
// ...
}
}
But I feel there has to be a more elegant solution than this. The format that makes the most sense to me is:
if let x = dict["key"] as? String {
// ...
}
But this just results in the error, mentioned above. I've tried dozens of variations of this, but none of it seems to make any difference. Is this something that just can't be done in Swift?
The reason your desired formulation isn't working is that you're trying to unwrap two Optionals with a single as?. There are two Optionals because both the subscripting of your dictionary and the attempted cast to String return optional values. There isn't a way to do it in one if statement that will be runtime-safe, but there is a way:
if let x = dict["key"]! as? String {
println(x)
}
The problem is that if dict["key"] ends up being nil, the forced unwrapping will crash your app. Better to do it this way (you can skip the first if), even if it is an extra step:
let x: AnyObject? = dict["key"]
if let y = x as? String {
println(y)
}
The extra step is just the cost of working with a Dictionary<String, AnyObject> - if you can get your dictionary type more specific, you won't have to do it any more.
I was also struggling with this thing but then i fount out that optional chaining is the solution. Just use
if let x = dict["key"]? as? String {
println(x)
}
It gives you both the safety and compactness.
In the end I had to use this one-line solution:
if let name = jsonDict["name"] as AnyObject? as? String {
println("name is \(name)")
} else {
println("property was nil")
}

Assign value to optional dictionary in Swift

I'm finding some surprising behavior with optional dictionaries in Swift.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else {
// Following line errors with "'Dictionary<String, String>?' does
// not have a member named 'subscript'"
foo["qux"] = "quux"
}
I've played with this a lot, trying to figure out what I might be missing, but nothing seems to make this code work as expected short of making the dictionary not optional. What am I missing?
The closest I can get is the following, but of course it's ridiculous.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
}
The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.
So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.
The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.
you can use this code
if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
} else {
foo = ["bar": "baz"]
}
with this code
var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""
error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^
the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.
one way to call method on optional is use ! i.e. foo![""], but...
var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""
error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~
whereas
var foo:Dictionary<String, String>? = Dictionary()
foo![""]
works
it is interesting these code failed to compile
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""
error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")
immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^ ~~~~~~~~~~~
the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it
so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)
and this code works
class MyDict
{
var dict:Dictionary<String, String> = [:]
subscript(s: String) -> String? {
get {
return dict[s]
}
set {
dict[s] = newValue
}
}
}
var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object
and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)
This is because your Dictionary is optional. If it's nil, you won't add an entry to it.
You can do this way:
var dict: [String : String]?
if let dict = dict {
dict["key"] = "value" // add a value to an existing dictionary
} else {
dict = ["key" : "value"] // create a dictionary with this value in it
}
Or, if you are given an optional dictionary, for example HTTPHeaders - which in AlamoFire is a [String : String] dictionary - and you want to either add a value if it's non-nil, or create it with this value if it's nil, you could do like so:
let headers: HTTPHeaders? // this is an input parameter in a function for example
var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
customHeaders["key"] = "value"
I tried this for Swift 3.1 and it worked:
if (myDict?[key] = value) == nil {
myDict = [key: value]
}

Upcasting F# record created through reflection

I've been messing about with F# and it's Reflection, trying to create a Record type object dynamically from within F#, I got most of it working (as you can see below) but one thing - the record I create through reflection has type "obj" instead the one it should ("Person") and I can't seem to be able to upcast it in any way.
#light
type Person = {
Name:string;
Age:int;
}
let example = {Name = "Fredrik"; Age = 23;}
// example has type Person = {Name = "Fredrik"; Age = 23;}
let creator = Reflection.FSharpValue.PrecomputeRecordConstructor(example.GetType(),
System.Reflection.BindingFlags.Public)
let reflected = creator [| ("thr" :> obj); (23 :> obj) |]
// here reflected will have the type obj = {Name = "thr"; Age = 23;}
// Function that changes the name of a Person record
let changeName (x:Person) (name:string) =
{ x with Name = name }
// Works with "example" which is has type "Person"
changeName example "Johan"
// But not with "reflected" since it has type "obj"
changeName reflected "Jack" // Error "This expression has type obj but is here used with type Person. "
// But casting reflected to Person doesn't work either
(reflected :> Person) // Type constraint mismatch. The type obj is not compatible with
// type Person. The type 'obj' is not compatible with the type 'Person'.
// C:\Users\thr\Documents\Visual Studio 2008\Projects\
// Reflection\Reflection\Script.fsx 34 2 Reflection
Try using the other cast operator (as you're casting the other way this time)
So changeName (reflected :?> Person) "Jack"

Resources