swift - create empty string : tuple dictionary using literal - dictionary

can anyone explain why line 1 works to create an empty swift dictionary but line 2 doesn't when i try to create a swift dictionary with int key and tuple of double values .... how should it be done?
var testDic2 = [Int:Double]()
var testDic3 = [Int:(Double,Double)]()
I've tried various combinations in playgrounds and the only version where it doesnt give me a compiler error is as follows
var possibleTips = [Int(): (tipAmt:Double(), total:Double())]
but im not sure this last form is declaring the dictionary as i intend it (ie as per testDict3 above)

The compiler isn't sure how to instantiate the type in your second and third example. Instead, you can declare the type and use an empty dictionary initializer:
var testDic:[Int:(Double,Double)] = [:]

Related

Swiftui: how do you assign the value in a "String?" object to a "String" object?

Swiftui dictionaries have the feature that the value returned by using key access is always of type "optional". For example, a dictionary that has type String keys and type String values is tricky to access because each returned value is of type optional.
An obvious need is to assign x=myDictionary[key] where you are trying to get the String of the dictionary "value" into the String variable x.
Well this is tricky because the String value is always returned as an Optional String, usually identified as type String?.
So how is it possible to convert the String?-type value returned by the dictionary access into a plain String-type that can be assigned to a plain String-type variable?
I guess the problem is that there is no way to know for sure that there exists a dictionary value for the key. The key used to access the dictionary could be anything so somehow you have to deal with that.
As described in #jnpdx answer to this SO question (How do you assign a String?-type object to a String-type variable?), there are at least three ways to convert a String? to a String:
import SwiftUI
var x: Double? = 6.0
var a = 2.0
if x != nil {
a = x!
}
if let b = x {
a = x!
}
a = x ?? 0.0
Two key concepts:
Check the optional to see if it is nil
if the optional is not equal to nil, then go ahead
In the first method above, "if x != nil" explicitly checks to make sure x is not nil be fore the closure is executed.
In the second method above, "if let a = b" will execute the closure as long as b is not equal to nil.
In the third method above, the "nil-coalescing" operator ?? is employed. If x=nil, then the default value after ?? is assigned to a.
The above code will run in a playground.
Besides the three methods above, there is at least one other method using "guard let" but I am uncertain of the syntax.
I believe that the three above methods also apply to variables other than String? and String.

Using Rascal MAP

I am trying to create an empty map, that will be then populated within a for loop. Not sure how to proceed in Rascal. For testing purpose, I tried:
rascal>map[int, list[int]] x;
ok
Though, when I try to populate "x" using:
rascal>x += (1, [1,2,3])
>>>>>>>;
>>>>>>>;
^ Parse error here
I got a parse error.
To start, it would be best to assign it an initial value. You don't have to do this at the console, but this is required if you declare the variable inside a script. Also, if you are going to use +=, it has to already have an assigned value.
rascal>map[int,list[int]] x = ( );
map[int, list[int]]: ()
Then, when you are adding items into the map, the key and the value are separated by a :, not by a ,, so you want something like this instead:
rascal>x += ( 1 : [1,2,3]);
map[int, list[int]]: (1:[1,2,3])
rascal>x[1];
list[int]: [1,2,3]
An easier way to do this is to use similar notation to the lookup shown just above:
rascal>x[1] = [1,2,3];
map[int, list[int]]: (1:[1,2,3])
Generally, if you are just setting the value for one key, or are assigning keys inside a loop, x[key] = value is better, += is better if you are adding two existing maps together and saving the result into one of them.
I also like this solution sometimes, where you instead of joining maps just update the value of a certain key:
m = ();
for (...whatever...) {
m[key]?[] += [1,2,3];
}
In this code, when the key is not yet present in the map, then it starts with the [] empty list and then concatenates [1,2,3] to it, or if the key is present already, let's say it's already at [1,2,3], then this will create [1,2,3,1,2,3] at the specific key in the map.

Shorthand dictionary with named tuples in Swift

It seems like you can't use named tuples in the shorthand notation of Dictionary. Is that so?
E.g.:
var dt = Dictionary<Int, (x:Double, y:Double)>()
var dtShort = [Int: (Double, Double)]()
var dtShortNamed = [Int: (x:Double, y:Double)]()
The first two lines work, the third triggers an error "Expected member name or constructor call after type name"
Is this correct, or am I missing something?
You are correct that it doesn't seem to work that way in Xcode 6 GM or Xcode 6.1 Beta 2.
It does work if you use a typealias though:
typealias NamedTuple = (x:Double, y:Double)
var dtShortNamed = [Int: NamedTuple]()
But, in that case, you might as well just use your first example:
var dt = Dictionary<Int, (x:Double, y:Double)>()

Can I insert into a map by key in F#?

I'm messing around a bit with F# and I'm not quite sure if I'm doing this correctly. In C# this could be done with an IDictionary or something similar.
type School() =
member val Roster = Map.empty with get, set
member this.add(grade: int, studentName: string) =
match this.Roster.ContainsKey(grade) with
| true -> // Can I do something like this.Roster.[grade].Insert([studentName])?
| false -> this.Roster <- this.Roster.Add(grade, [studentName])
Is there a way to insert into the map if it contains a specified key or am I just using the wrong collection in this case?
The F# Map type is a mapping from keys to values just like ordinary .NET Dictionary, except that it is immutable.
If I understand your aim correctly, you're trying to keep a list of students for each grade. The type in that case is a map from integers to lists of names, i.e. Map<int, string list>.
The Add operation on the map actually either adds or replaces an element, so I think that's the operation you want in the false case. In the true case, you need to get the current list, append the new student and then replace the existing record. One way to do this is to write something like:
type School() =
member val Roster = Map.empty with get, set
member this.Add(grade: int, studentName: string) =
// Try to get the current list of students for a given 'grade'
let studentsOpt = this.Roster.TryFind(grade)
// If the result was 'None', then use empty list as the default
let students = defaultArg studentsOpt []
// Create a new list with the new student at the front
let newStudents = studentName::students
// Create & save map with new/replaced mapping for 'grade'
this.Roster <- this.Roster.Add(grade, newStudents)
This is not thread-safe (because calling Add concurrently might not update the map properly). However, you can access school.Roster at any time, iterate over it (or share references to it) safely, because it is an immutable structure. However, if you do not care about that, then using standard Dictionary would be perfectly fine too - depends on your actual use case.

Swift dictionary bug?

So I started a project in Swift, and I've come to this problem:
this code works:
var dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
println(dictionary)
dictionary["c"] = "newValOfC"
println(dictionary)
and this doesn't:
var dictionary = [:]
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
println(dictionary)
dictionary["c"] = "newValOfC"
println(dictionary)
Gives an error:
Playground execution failed: error: <REPL>:35:17: error: cannot assign to the result of this expression
dictionary["c"] = "newValC"
~~~~~~~~~~~~~~~ ^
Notice that this is not a constant value
So why doesn't the line
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
give an error?
Since the context does not provide enough information to infer the type, you'll need to explicitly name it as a dictionary, otherwise swift assumes it is an NSDictionary (I'm not clear on why though. I assume for better obj-c compatibility):
The following code all works:
// Playground
import UIKit
var str:NSString = "Hello, playground"
var d0 = [:]
var d1: Dictionary = [:]
d0.setValue(UIWebView(), forKey: "asdf")
d1["asdf"] = 1
d1["qwer"] = "qwer"
Okay, I found it, the problem is that by initializing an empty dictionary, the type inference gets a little crazy.
You'll need this code:
var dictionary = Dictionary<String, String>()
instead of
var dictionary = [:]
but that still does not explain why the line
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
does not give an error
And referring to Swift Language Guide, the
dictionary = [:]
syntax is correct "if the context already provides type information".
The big difference is that
var dictionary = [:]
doesn't give any chance the compiler to infer the right type, whereas
var dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
does.
Actually it looks like the first line produces a __NSDictionaryI instance, whereas the second one produces a Dictionary<String,String as expected.
From The Swift Programming Language book
If the context already provides type information, create an empty dictionary with an empty dictionary literal, which is written as [:] (a colon inside a pair of square brackets):
which means the first line is ambiguous.
The [:] syntax only works if the current context has enough information to be able to infer what the types are. Here are a few ways where the context is known...
As has been mentioned already, the code below works because it can be inferred that the keys and values are strings:
var dictionary = [ "a" : "valueOfA" , "b" : "valueOfB" , "c" : "valueOfC" ]
A dictionary passed into a function has a known context:
func dictionaryFunc(var dictionary : Dictionary<String, String>) {
// Do stuff with the dictionary
}
In both cases, the key/value types for the dictionary are now known, so you could do this to create a new dictionary with the same variable name...
dictionary = [:]
In the first case, if you used the [:] syntax after the declaration you would be erasing the contents of the original dictionary. In the second case, dictionaries get copied when they get passed into functions, so using [:] would only be erasing the copy.
In Swift 2.0 I'm using this:
var dictionary = [String:String]()
dictionary["key1"] = "value1"

Resources