Swift - Working with Dictionaries - Add multiple values - macos

I've been looking for answers to this problem, but unfortunately without success.
I'm developing a mathematical app (Swift-based), which keeps data of every function the user enters.
(I then need to draw the functions on an NSView using a Parser)
The data structure is saved into a Dictionary but I'm not able to add values and keys.
The Dictionary is initialized like:
var functions = [String : [[String : NSBezierPath], [String : NSColor], [String : CGFloat], [String : Bool]]]();
//1A.The String key of the main Dictionary is the value of the function, such as "sin(x)"
//1B.The value of the `Dictionary` is an `Array` od `Dictionaries`
//2.The first value is a dictionary, whose key is a String and value NSBezierPath()
//3.The second value is a dictionary, whose key is a String and value NSColor()
//4.The third value is a dictionary, whose key is a String and value CGFloat()
//5.The first value is a dictionary, whose key is a String and value Bool()
To add the functions, I have implemented a method (I will report a part of) :
...
//Build the sub-dictionaries
let path : [String:NSBezierPath] = ["path" : thePath];
let color : [String:NSColor] = ["color" : theColor];
let line : [String:CGFloat] = ["lineWidth" : theLine];
let visible : [String:Bool] = ["visible" : theVisibility];
//Note that I'm 100% sure that the relative values are compatible with the relative types.
//Therefore I'm pretty sure there is a syntax error.
//Add the element (note: theFunction is a string, and I want it to be the key of the `Dictionary`)
functions[theFunction] = [path, color, line, visible]; //Error here
...
I'm given the following error:
'#|value $T10' is not identical to '(String,[([String:NSbezierPath],[String : NSColor],[String : CGFloat],[String : Bool])])'
I hope the question was enough clear and complete.
In case I will immediately add any kind of information you will need.
Best regards and happy holidays.

Dictionaries map from a specific key type to a specific value type. For example, you could make your key type String and your value type Int.
In your case, you’ve declared quite a strange dictionary: a mapping from Strings (fair enough), to an array of 4-tuples of 4 different dictionary types (each one from strings to a different type).
(It’s a new one on me, but it looks like this:
var thingy = [String,String]()
is shorthand for this:
var thingy = [(String,String)]()
Huh. Strange but it works. Your dictionary is using a variant of this trick)
This means to make your assignment work you need to create an array of a 4-tuple (note additional brackets):
functions[theFunction] = [(path, color, line, visible)]
I’m guessing you didn’t mean to do this though. Did you actually want an array of these 4 different dictionary types? If so, you’re out of luck – you can’t store different types (dictionaries that have different types for their values) in the same array.
(Well, you could if you made the values of the dictionary Any – but that’s a terrible idea and would be nightmare to use)
Probably the result you wanted was this (i.e. make the functions dictionary map from a string to a 4-tuple of dictionaries of different types):
var functions = [String : ([String : NSBezierPath], [String : NSColor], [String : CGFloat], [String : Bool])]()
You’d assign values to the dictionary like this (note, no square brackets on the rhs):
functions[theFunction] = (path, color, line, visible)
This will work but it will be pretty unpleasant to work with. But do you really want to store your structured data in dictionaries and arrays? This isn’t JavaScript ;-) You’ll tie yourself in knots navigating that multi-level dictionary. Declare a struct! It’ll be so much easier to work with in your code.
struct Functions {
var beziers: [String:NSBezierPath]
var color: [String:NSColor]
var line: [String:NSColor]
var floats: [String:CGFloat]
var bools: [String:Bool]
}
var functions: [String:Functions] = [:]
Even better, if all the beziers, colors etc are supposed to be references with the same key, declare a dictionary that contains all of them or similar.

Related

How to decrease arguments in elm?

I'm new to Elm (version 0.19).
On thing that's bugging me is the huge list of arguments I'm passing around. I think the problem is due to my OOP way of thinking. In my code I have a bunch of helper functions that require access to my model (TEA). I have been using let / in syntax in the view function to define these helpers as this gives them access to the model argument. However I have 10+ helper functions and I'm constantly passing them around, it makes my code look ugly and hard to comprehend. In OOP these helper functions would all be methods on some object that I would pass instead.
Code snippet below is a contrived example that uses elm-ui. Full example can be run on Ellie
Element.layout []
<| column
[ w |> px |> width
, h |> px |> height
, blueBg
, centerX
, centerY
]
[ el [centerX, centerY, whiteTxt, fontSize 40] <| text "Hello world"
, header w h scale whiteTxt space blueBg pad radius whiteBg fontSize blackTxt greyBg blueTxt
]
header w h scale whiteTxt space blueBg pad radius whiteBg fontSize blackTxt greyBg blueTxt =
-- code here
el [] Element.none
Whats the best way to do this?
There are several strategies for this:
Use types to abstract away data complexity. Model your problem using types, and then create simple functions to operate on those types.
Define constants and helper functions at higher levels so that they are in scope for functions, rather than passing them in as a parameter.
Narrow down function parameters to only what is strictly needed for that function.
In the header example, you might provide your own types for the style, title and subtitle:
type alias Title = String
type alias SubTitle = String
header : HeaderStyle -> Title -> SubTitle -> Element Msg
When you do have lots of information to pass around, records are usually the first go-to for holding data. For example, if you're reusing the header function multiple times with different styling, then a HeaderStyle record type might be useful:
type alias HeaderStyle = { borderColor : Color
, textColor: Color
, backgroundColor : Color
...
}
Now since the title and subtitle are part of the header you could also pull it all into one record:
type alias HeaderData = { borderColor : Color
, textColor: Color
, backgroundColor : Color
, bannerText : String
, bannerImage : Url
, headerTitle : Title
, headerSubTitle : SubTitle
...
}
If we pay attention to the types supplied by the library, we can see that for elm-ui it might make more sense to keep the styles in a list to match their types. Modifying our record, we get:
type alias HeaderData = { styleList : List (Attribute Msg)
, headerTitle : Title
, headerSubTitle : SubTitle
...
}
The advantage here is that we can extract the style list and use it directly in a function from the Element module by calling the automatic function styleList: HeaderData -> List (Attribute Msg).
The disadvantage is that we've lost our nice compiler error message if someone makes a HeaderData missing some key styles.
Either way, now our header function is down to only one input.
header : HeaderData -> Element Msg
Great, but this still means that every time we run header we need to populate a HeaderData. How can we simplify that?
One strategy is to use constants at the top level, and use helper functions / partial application to apply these constants.
We can for example define a top level constant and helper function that lets us create various predefined headers:
pageStyle : List (Attribute Msg)
pageStyle = [ Border.color <| rgb255 35 97 146
, Font.color <| rgb255 35 97 146 ]
redHeaderStyle : Title -> HeaderData
redHeaderStyle title =
{ styleList : pageStyle
++ [ Background.color red
, Font.color black
...
]
, headerTitle : title
}
Here pageStyle can be used elsewhere, and readHeaderStyle adds to it for this specific case. Note that we've left one parameter for later, since the title may change for each application.
Of course in Elm you are not restricted to lists and records - you could also use product and sum types for Header. When to use each type is going to depend on things like where you want the type safety and how you want to compose functions.
So to summarize:
Start by modelling your problem domain using types, rather than thinking in terms of objects or components.
Don't try to make everything generic and reusable. Instead create types that hold information, and use helper functions so you only have to worry about what's relevant.
Don't be afraid of defining constants and helper functions at the top-level. Limit your use of let..in constructs to improving readability, rather than for grouping definitions as though they're methods.
Write helper functions for your types on an as-needed basis, rather than trying to create a library of every possible functionality.
If you want to compartmentalise similar functions and types, you can use modules and then import them qualified to a convenient name. This way you can say e.g. Background.color and Element.row.
Finally, Scott Wlashchin gives a good talk on design strategies in functional programming: Functional programming design patterns by Scott Wlaschin

How to provide optional parameters in Go?

I'm coming from a Node.js background, and there a typical pattern is to have a function which takes an options object, i.e. an object where you set properties for optional parameters, such as:
foo({
bar: 23,
baz: 42
});
This is JavaScript's "equivalent" to optional and named parameters.
Now I have learnt that there are no optional parameters in Go, except variadic parameters, but they lack the readability of named parameters. So the usual pattern seems to be to hand over a struct.
OTOH a struct can not be defined with default values, so I need a function to set up the struct.
So I end up with:
Call a function that creates the struct and then fills it with default values.
Overwrite the values I would like to change.
Call the function I actually want to call and hand over the struct.
That's quite complicated and lengthy compared to JavaScript's solution.
Is this actually the idiomatic way of dealing with optional and named parameters in Go, or is there a simpler version?
Is there any way that you can take advantage of zero values? All data types get initialized to a zero value, so that is a form of default logic.
An options object is a pretty common idiom. The etcd client library has some examples (SetOptions,GetOptions,DeleteOptions) similar to the following.
type MyOptions struct {
Field1 int // zero value (default) of int is 0
Field2 string // zero value (default) of string is ""
}
func DoAction(arg1, arg2 string, options *MyOptions){
var defaultValue1 int = 30 // some reasonable default
var defaultValue2 string = "west" // some reasonable default
if options != nil {
defaultValue1 = options.Field1 // override with our values
defaultValue2 = options.Field2
}
doStuffWithValues
An relevant question (and very much in the mindset of Go) would be, do you need this kind of complexity? The flexibility is nice, but most things in the standard library try to only deal with 1 default piece of info/logic at a time to avoid this.

Is Swift type-inference contradicting itself here?

Here's my test code:
var myDict: [String: AnyObject] = ["k":"v"]
var a = myDict["k"]
var b = a as String
var c = myDict["k"] as String
Here's my Swift playground in Xcode6-beta6:
According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?
I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:
var myDict: [String: Any] = ["k":"v"]
then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.
Note that if you change the last line to:
var c = myDict["k"] as NSString
the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.
Update:
So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:
So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.
This has to do with the fact that Dictionary has two subscript overloads:
subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }
The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.
The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.
When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.
This works.
var c = myDict["k"] as AnyObject! as String // "v"
To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:
var c = myDict["k"] as AnyObject? as String // "v"
Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.

Getting Hashtable keys in intellisense

I am using hashtable that contains around 60 key,value pairs.
for assigning the value based on key to any control in my page i have to explicitly type the keyname.
For example:
txtName.Text = htData["Name"].ToString();
txtAddress.Text = htData["Address"].ToString();
Doing this same thing for 60 values is time taking and inefficient.
is there a way to directly get the key and then set the value.
Just like intellisense in vs.
For example:
txtName.Text = htData.Name.Value.ToString();
txtAddress.Text = htData.Address.Value.ToString();
If you are stuck with the Hashtable there is not much you can do. I would suggest switching to the Dictionary class.
You could then make the Key an Enumeration:
public enum DataType
{
Name,
Adress,
...
}
Or you could make an Object with 60 Properties, without using a Dictionary.
If you are stuck with the Hashtable, you could still use an Enumeration, with ToString().
A Hashtable will never provide "nice" code though.

Parsing NSString to get data out

I have this code...
NSData* myData = producedData;
NSLog(#"Contents of myData: %#", myData);
The log prints
{
"id" = "";
"level" = "level_1";
"handle" = test;
}
How do I get the values for id and level and handle out of this? The original data is a NSString*.
Thanks!
Is it JSON? Use Stig Brautaset's JSON parser http://code.google.com/p/json-framework/
You aren't showing the code that actually obtains the data object, nor are you showing any code related to an NSString.
Are you just assigning a string (producedData) to your myData variable? That won't create a data object; for one thing, it wouldn't know what encoding to use to encode the string's characters into bytes, and more importantly, copying a pointer from one variable to another (which is what myData = producedData does—the variables do not contain the objects themselves, only pointers to them) does not change anything about what the pointer points to. The object will remain a string, even though you told the compiler that myData would point to a data object. The compiler should be warning you about this; you should heed and fix those warnings.
myData definitely is not a data object; if it were, its description of itself would be a hex dump. It is either a string or a dictionary.
The output you showed matches the syntax that an NSDictionary uses to describe itself. On the other hand, the object could be a string containing such a description. (This latter case is what you're expecting.)
If you have a dictionary: You're done! The object is already parsed.
If you have a string: Send it a propertyList message, which will parse the string as a property list and return whatever value is represented in it, which, in this case, will be a dictionary.

Resources