Initialize a dictionary with non-optional empty arrays in Swift [duplicate] - swift

This question already has answers here:
println dictionary has "Optional"
(2 answers)
Closed 7 years ago.
I'm trying to associate an array numerical data with a column label, so I thought I'd model that with a dictionary that maps a string to an array of floats. I'm trying to populate the dictionary by initializing the key values with empty arrays, but I keep getting optional arrays instead. Here is an example,
var labels = [ "pine", "elm", "palm" ]
var dframe = [String:[Float]]()
for label in labels {
dframe[label] = [Float]()
}
print(dframe.dynamicType) // --> Dictionary<String, Array<Float>>
print(dframe["pine"].dynamicType) // --> Optional<Array<Float>>
I confused about why the initialization [Float]() in the for-loop produces an optional array. If I say [Float]()! in the for-loop, then I get an error saying that I "cannot force unwrap value of non-optional type '[Float]'". Any ideas? I just want a simple, non-optional mapping of strings to arrays of floats. I'm using XCode 7.1.1, and Swift 2.1. Thanks!

When you access a dictionary with:
let x = dframe["pine"]
The compiler didn't know if you pass a valid key to the dictionary, if you didn't, it will return nil. The optional part is not in the array initialization, but in the value search of the dictionary.

Related

Arrays & type-safety using optionals, best practices?

I am attempting to understand the difference between declaring the following optional arrays.
Question 1) In the below example imagine the arrays were appended with a nil value, what would occur & which variable declarations would not crash?
Question 2)similarly imagine we swapped out the type String with type someObject. If the object failed to initialize correctly and returned nil, what would occur & which variable declarations would not crash?
I believe the answer for both 1,2) is var three & var four.
I also think var one & var two can work if they are initialized () but I am not sure.
I am fairly certain var five & var six will crash when attempting to append the array.
Question 3) Assuming that I went with option var one & var two & they were initialized as a empty array, can I add nil/empty objects/strings?
Question 4) Assuming that I went with option var three & var four, would it be advisable to use compactmap to filter out nil values?
Question 5) Assuming I went with option var five and var six, would it be advisable to check for nil/empty object/strings using nil coalescing or a guard statement to check for a value?
var one : Array<String?> //array containing optional strings
//is the same type as
var two : [String?] //array containing optional strings
var three : Array<String?>? //optional array containing optional strings
//is the same type as
var four : [String?]? //optional array containing optional strings
var five : Array<String>? //optional array containing strings
//is the same type as
var six : [String]? //optional array containing strings
I inherited a codebase where I am receiving a json response and using the coddle protocol to create a object based on a struct model. I noticed that the variables were declared.
var seven : [someModel]?
The model is
struct someModel {
var a : String?
}
Question 6) Am I correct to assume that because all the properties/attributes of the struct are optionals, a object representing the struct can safely be appended to var seven even if someModel.a = optional(nil)?
First, none of these will crash. nil is not anything like Java's NULL (or C's NULL, and almost nothing like ObjC's nil). Accessing nil is completely safe. It's just a value, like every other value. nil is just a piece of syntactic sugar around Optional.none. They are exactly the same. Optionals are just enums:
enum Optional<Wrapped> {
case some(Wrapped)
case none
}
There is also syntactic sugar so that you can write Array<T> as [T]. These are exactly the same thing.
Similarly, you can write Optional<T> as T?. These, again, are exactly the same thing.
var one : Array<String?> //array containing optional strings
var two : [String?] //array containing optional strings
These are arrays of Optional<String>. Each element can be .some(string) or it can be .none.
var three : Array<String?>? //optional array containing optional strings
var four : [String?]? //optional array containing optional strings
As you say, these are optional arrays of optional strings. They can be .none or .some(array). In that array is identical to one and two above.
var five : Array<String>? //optional array containing strings
var six : [String]? //optional array containing strings
As you say, this is an optional array of strings. So it can be .none or it can be .some(array) where array is an array of strings (not optional strings).
Question 6) Am I correct to assume that because all the properties/attributes of the struct are optionals, a object representing the struct can safely be appended to var seven even if someModel.a = optional(nil)?
Yes, though it's not optional(nil); that would be an String?? rather than a String?. It's just nil.
The only place that optionals will crash due to being nil is if you evaluate them with a ! at the end. The only reason this crashes is because it literally means "please crash the program if this is nil," so don't use ! unless you mean that.
This isn't really a best-practices question, but since you titled it that way, a few points:
Type names should always have a leading capital letter.
As a rule, I discourage optional collections. That includes Array? and String?. You need to ask: how is "no value" different than "empty value." If they're the same (and they usually are), then you should just use empty values. So instead of nil, just use "" and []. Then all the optionals go away, and the code becomes much simpler.

Print contents of array with custom class swift [duplicate]

This question already has answers here:
Overriding description method in NSObject on swift
(1 answer)
How can I change the textual representation displayed for a type in Swift?
(7 answers)
Closed 5 years ago.
I have an array of type custom class as seen below. It has strings, ints, and double values.
class TrendData: NSObject {
var earlyTime = Date()
var recentTime = Date()
var earlyTimePrice = Double()
var recentTimePrice = Double()
}
I have an array as follows
let dataArray = [TrendData]()
I have filled in 2 values into the dataArray.
Right now when I use a print command as follows
print ("Label: \(dataArray)")
it prints this Label: [<App.TrendData: 0x600000472440>] ** [<App.TrendData: 0x600000472440>]
This is the correct behavior but I want to see all the values within each of these elements in the array. What is the best way to do that? I don't want to explicitly list out each element or even put it into a loop if I can avoid it. I am able to do it, it is just really messy right now. Is there any function or command that does this automatically?

How to iterate over an array and check element type at the same time in swift? [duplicate]

This question already has answers here:
For-in loop and type casting only for objects which match type
(2 answers)
Closed 6 years ago.
I'd like to know if it is possible to iterate over an array is swift and check if the current element has a certain type.
A use case would be : I have an array with NSDictionarys and NSArrays and I ony want to take care of the NSDictionarys.
I'd like to do this without the condition inside the loop :
for entity in array
{
if entity is NSDictionary
{
// Do something
}
}
I'm looking for something like :
for (entity in array) as? NSDictionary
{
// Do something
}
Of course I know that what I have just written doesn't work, but I guess you can get the idea of what I'm asking for.
Any idea would be really appreciated !
Solution 1
You can filter your array
let things = [NSDictionary(), NSArray(), NSDictionary(), NSArray()]
let dicts = things.flatMap { $0 as? NSDictionary }
Now dicts is defined as [NSDictionary] an contains only the 2 dictionaries.
Solution 2
You can also perform a for loop only on the values that are dictionaries
let things = [NSDictionary(), NSArray(), NSDictionary(), NSArray()]
for dict in things.flatMap( { $0 as? NSDictionary }) {
}
Using filter with the type check operator is
As an alternative to the flatMap solutions, you may use filter along with the type check operator is:
let arr: [AnyObject] = [NSDictionary(), ["foo", "bar"], NSDictionary(), "foo"]
for dict in arr.filter({ $0 is NSDictionary }) {
// do something with dictionary
}
Use the type check operator (is) to check whether an instance is of
a certain subclass type. The type check operator returns true if the
instance is of that subclass type and false if it is not.
From Swift Language Guide - Type Casting. Since you don't have to worry about subclass matches (you know the elements to be either NSDictionary or NSArray) the is filter approach can be an appropriate alternative here.
Note that when using the flatMap and type cast operator (as), the iterate element in the for loop (dict) will be of type NSDictionary, whereas the filter approach above will perform no downcasting, hence preserving the NSDictionary in its wrapped form, as AnyObject. Probably the flatMap approach is to prefer here.
If you don't care for elements to have the same indexes as in original array, you can flatMap to the required type.
for element in array.flatMap({ $0 as? NSDictionary }) {
// do something
}

How to initialize an array inside a dictionary?

I have a dictionary declared like this:
var myDict = [Int : [Int]]()
The array in it is not initialized, so every time I have to check it first:
if (myDict[idx] == nil) {
myDict[idx] = []
}
How to initialize it as an empty array in MyDict declaration?
I think you could be misunderstanding something pretty key - let's make sure:
The way the dictionary works, is not to have one array, but an array for each key.
Each value of 'idx' you request the array for returns a different array.
You can't expect it to return an empty array - a dictionary is meant to return a nil value for a key that hasn't been set. To do what you're trying, the following would probably do the trick:
myDict[idx] = myDict[idx] ?? []
That's what dictionary do if you try to retrieve a key for which no value exists.
You can subclass Dictionary and override the subscript func to get what you want like this. Or simply write an extension, or write a operator defination to use a different symbol.
“You can also use subscript syntax to retrieve a value from the dictionary for a particular key. Because it is possible to request a key for which no value exists, a dictionary’s subscript returns an optional value of the dictionary’s value type. ”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks. https://itunes.apple.com/cn/book/swift-programming-language/id881256329?l=en&mt=11
You can initialize it this way
myDict.forEach {
(var dictElement) -> () in
dictElement.1 = [Int]()
}

Why does SWIFT print "Optional(...) [duplicate]

This question already has answers here:
println dictionary has "Optional"
(2 answers)
Closed 7 years ago.
If the following code runs
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
print(airports["YYZ"])
Why does the console print
Optional("Toronto Pearson")
Why does it print Optional( withValue ) and not just the value?
Why would I need to know that in the console?
Swift has optional types for operations that may fail. An array index like airports["XYZ"] is an example of this. It will fail if the index is not found. This is in lieu of a nil type or exception.
The simplest way to unwrap an optional type is to use an exclamation point, like so: airports["XYZ"]!. This will cause a panic if the value is nil.
Here's some further reading.
You can chain methods on option types in Swift, which will early exit to a nil without calling a method if the lefthand value is nil. It works when you insert a question mark between the value and method like this: airports["XYZ"]?.Method(). Because the value is nil, Method() is never called. This allows you to delay the decision about whether to deal with an optional type, and can clean up your code a bit.
To safely use an optional type without panicking, just provide an alternate path using an if statement.
if let x:String? = airports["XYZ"] {
println(x!)
} else {
println("airport not found")
}