Native Swift Dictionary by Reference - swift

What I'm trying to do:
a class that has several (say 10) instance variables of dictionary type (mutable var).
a method that (depending on arguments, etc.) picks a dictionary an updates it.
In ObjC, this is fairly easily accomplished using NSMutableDictionary. In Swift, this is more tricky, since the dictionary gets copied on into the local variable.
I think the best way to explain what I'm trying to achieve is via a code sample:
class MyClass {
/// There are several dictionaries as instance variables
var dict1: [String : String] = [ : ]
var dict2: [String : String] = [ : ]
// ...
/// This method should change a value in one of the dictionaries,
/// depending on the argument.
func changeDictAtIndex(index: Int) {
var dict: [String : String]
if index == 0 {
dict = dict1
}else{
dict = dict2
}
dict["OK"] = "KO"
// This won't work since Swift copies the dictionary into
// the local variable, which gets destroyed at the end of
// the scope...
}
}
let obj = MyClass()
obj.changeDictAtIndex(0)
obj.dict1 // Still empty.
Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
P.S.: I'm aware of the inout attribute, but that works AFAIK only with function arguments, which doesn't really solve anything...
EDIT:
I'm currently solving this via closure:
var dictSetter: (key: String, value: String) -> Void
if index == 0 {
dictSetter = { self.dict1[$0] = $1 }
}else{
dictSetter = { self.dict2[$0] = $1 }
}
dictSetter(key: "OK", value: "KO")

As you may already aware, you can use inout to solve the problem
func updateDict(inout dict: [String : String]) {
dict["OK"] = "KO"
}
func changeDictAtIndex(index: Int) {
if index == 0 {
updateDict(&dict1)
}else{
updateDict(&dict2)
}
}

Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
I have rewritten your class, note the changes:
Different syntax for empty dictionary
ChangeDictAtIndex function now takes in a dictionary you want to replace.
The instance variables are being set to the passed in dict.
I would look at the Apple's The Swift Programming Language (Swift 2.1) section on the basics and collection types.
class MyClass {
// There are several dictionaries as instance variables
var dict1 = [String : String]()
var dict2 = [String : String]()
func changeDictAtIndex(index: Int, dict: [String : String]) {
if index == 0 {
dict1 = dict
} else {
dict2 = dict
}
}
}
Usage:
let obj = MyClass()
obj.changeDictAtIndex(0, dict: ["MyKey" : "MyValue"])
print(obj.dict1)

Related

Swift: How to keep updating a Dictionary inside another Dictionary correctly?

This is a little hard to explain, but I'll try my best. I am trying to update a Dictionary inside another Dictionary properly. The following code almost does what I need.
var dictionary = Dictionary<String, [Int : Int]>()
func handleStatsValue(tag: Int ) {
let currentValue: Int = dictionary["Score"]?[tag] ?? 0
dictionary["Score"] = [
tag : currentValue + 1
]
}
However, it seems the dictionary is overridden when the tag value changes (e.g. 1 to 2). I need Dictionary to have multiple dictionaries inside of it. Any tips or suggestions are deeply appreciated.
Edit: I'm trying to have multiple dictionaries nested inside a dictionary. It seems whenever the tag value is changed, the dictionary is overridden.
One way to write this would be:
func handleStatsValue(tag: Int) {
dictionary["Score", default: [:]][tag, default: 0] += 1
}
or, written without [_:default:]
func handleStatsValue(tag: Int) {
var scoreDictionary = dictionary["Score"] ?? [:]
scoreDictionary[tag] = (scoreDictionary[tag] ?? 0) + 1
dictionary["Score"] = scoreDictionary
}
However, it's not a good idea to use nested dictionaries to keep your data. Use a custom struct instead and try to avoid tags too:
struct DataModel {
var score: [Int: Int] = [:]
}
I think you need something like this to either increase the value for an existing tag or add a new tag if it doesn't exist
func handleStatsValue(tag: Int ) {
if var innerDict = dictionary["Score"] {
if let value = innerDict[tag] {
innerDict[tag] = value + 1
} else {
innerDict[tag] = 1
}
dictionary["Score"] = innerDict
}
}
Although the code looks a bit strange with the hardcoded key "Score", maybe it would be better to have multiple simple dictionaries instead, like
var score: [Int, Int]()
or if you prefer
var score = Dictionary<Int, Int>()

Updating variable value from dictionary and best practice

As I progress into my Swift education, the time as come for me to ask for help about best practice and code optimization.
My app has become more and more complex every day and the current situation is as follows: I started using dictionaries and arguments to use a single function that can process a lot of different variables depending on the situation, which seems to be better practice than using 5 different functions that will do the same thing only with different variables.
I now have two files as follows:
Main1.swift:
class Main1 {
static var value1 : Int = 1
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1]
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(value)
}
}
Main2.swift:
class Main2 {
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1] // <- thinking `value1` would now be 2
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to get 3 here
print(value)
}
}
These classes are simplified versions of my code but the logic is the same: I am trying to use variables loaded from dictionaries and update their values to be used in another file and function:
Main1().updateValue("value1") //2
Main2().updateValue("value1") //2 <--- I need 3 here!
-> What exactly am I trying to achieve here?
To update the reference (static var value1 : Int = 1) value while accessing it through the convenience of a dictionary (or different method but you get the point about convenience).
In fact I am trying to do Main1.value1 = Main1.value1 + 1 while accessing Main1.value1 through a dictionary, which is impossible because I am not accessing the reference here.
I know this can't work, I have 3 different copies of value here but I don't how to update the variable value without using another global variable... I need your help to find a better logic.
I am open to any suggestion or thinking. I am not asking for code solution (which would be great anyway) but I'd love to have my education re-centered a little bit, I am starting to lose myself learning all by myself and the frustration comes from that I don't know what to be looking for anymore.
EDIT BASED ON COMMENTS
As per the comments below, here's a potential solution:
class Main1 {
static var dict: [String: Int] = ["value1": 1]
func updateValue(_ key: String) {
guard var value = dict[key] else { return }
value = value + 1
print(value)
dict[key] = value
}
}
ORIGINAL ANSWER
In Swift, [String : Int], String and Int are value types, as opposed to their Objective-C counterparts NSDictionary, NSString and NSNumber, which are reference types.
This means that when you do guard var value = dict1 else { return }, value is now a copy of what the dictionary contained, not a reference to that piece of data inside the dictionary.
So when you do value = value + 1 you're setting the new variables value, but not the contents of the dictionary.
Following your logic, you need to put value back into the dictionary, like this:
func updateValue(_ value: String) {
var dict : [String : Int] = ["value1": Main1.value1] // <- Change this to a var
let dict1 = dict[value]
guard var intValue = dict1 else { return }
intValue = intValue + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(intValue)
dict[value] = intValue // <- ADD THIS
}

Check if dictionary contains value in Swift

Just simple task. I've got a dictionary var types = [Int : String]() which inits like an empty and after some user actions it fills with data. According to emptiness or some specific data in this dictionary I enable/disable a button in UI.
Check for emptiness is easy, but how to check if dictionary contains certain value?
Compiler suggested me a placeholder with predicate:
types.contains(predicate: ((Int, String)) throws -> Bool>)
Since you only want to check for existance of a given value, you can apply the contains method for the values properties of your dictionary (given native Swift dictionary), e.g.
var types: [Int : String] = [1: "foo", 2: "bar"]
print(types.values.contains("foo")) // true
As mentioned in #njuri: answer, making use of the values property of the dictionary can seemingly yield an overhead (I have not verified this myself) w.r.t. just checking the contains predicate directly against the value entry in the key-value tuple of each Dictionary element. Since Swift is fast, this shouldn't be an issue, however, unless you're working with a huge dictionary. Anyway, if you'd like to avoid using the values property, you could have a look at the alternatives given in the forementioned answer, or, use another alternative (Dictionary extension) as follows:
extension Dictionary where Value: Equatable {
func containsValue(value : Value) -> Bool {
return self.contains { $0.1 == value }
}
}
types.containsValue("foo") // true
types.containsValue("baz") // false
I wrote a function which is using contains method on dictionary.
Your specific case:
let dic : [Int : String] = [1 : "a", 2 : "b"]
func dictionary(dict : [Int : String], containsValue value : String)->Bool{
let contains = dict.contains { (_,v) -> Bool in
return v == value
}
return contains
}
let c = dictionary(dic, containsValue: "c") // false
let a = dictionary(dic, containsValue: "a") // true
Generic:
extension Dictionary{
func containsValue<T : Equatable>(value : T)->Bool{
let contains = self.contains { (k, v) -> Bool in
if let v = v as? T where v == value{
return true
}
return false
}
return contains
}
}
I've tested this function against dictionary.values.contains() and it is roughly two times faster.
If you want to check if already contains a value this would be the way:
if !yourDictionary.values.contains("Zero") {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("this value already exists");
}
And this one if you want to check if the key exists:
You get the item to add to your dictionary.
Check if the item's key already exists
If it doesn't, append the item or enable the button.
//1
let newItemKey = 0
let newItemValue = "Zero"
//2
let keyExists = yourDictionary[newItemKey] != nil
//3
if !keyExists {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("This key already exists");
}
The dictionary getter returns an optional value.
let dictionary = ["ben": "says hi"]
let containsAlpha = dictionary["alpha"] != nil
let containsBen = dictionary["ben"] != nil

How to assign a reference to a Dictionary in Swift

I am working with a complex dictionary and want to make it easy to work just assigning a variable to it.
myDictionay["with"]["complex"]["sub"]["dictionary"] = "NewValue"
I just want this:
let smaller = myDictionay["with"]["complex"]["sub"]
smaller["dictionary"] = "NewValue"
How can I do it?
The Swift Dictionary (and Array / Set) follows pass by reference semantics, rather than pass by value semantics (if you look in the headers you'll see it is a struct, not a class). This means that when you assign a Dictionary instance from one variable to another variable, and change the value associated with the new variable, it does not in fact change the value associated with the original value. As such, the syntax you are looking for is not possible with a Swift Dictionary. Having said that, you can always use an NSMutableDictionary instead, and then the syntax you are hoping for will work.
You could use a closure to perform the inner access for you:
let smaller : (inout _: [String : [String : [String :[String : String]]]], key: String, val: String) -> () = { dict, key, val in
dict["with"]?["complex"]?["sub"]?[key] = val
return ()
}
/* setup example */
var a = [String : String]()
var b = [String :[String : String]]()
var c = [String : [String : [String : String]]]()
var myDictionary = [String : [String : [String :[String : String]]]]()
a["dictionary"] = "OldValue"
b["sub"] = a
b["anothersub"] = a
c["complex"] = b
myDictionary["with"] = c
/* example */
print(myDictionary)
/* ["with": ["complex": ["anothersub": ["dictionary": "OldValue"],
"sub": ["dictionary": "OldValue"]]]] */
smaller(&myDictionary, key: "dictionary", val: "NewValue")
print(myDictionary)
/* ["with": ["complex": ["anothersub": ["dictionary": "OldValue"],
"sub": ["dictionary": "NewValue"]]]] */
Or, more condensed: you can use a closure specifically with a dictionary name accessible in the scope where closure is used (i.e., no need to send a reference to the dictionary as an argument to the closure).
let smaller2 : (String, String) -> () = { myDictionary["with"]?["complex"]?["sub"]?[$0] = $1 }
smaller2("dictionary", "NewerValue")
print(myDictionary)
/* ["with": ["complex": ["anothersub": ["dictionary": "OldValue"],
"sub": ["dictionary": "NewerValue"]]]] */
If you're handling your dictionary myDictionary as some class property, you could, as an alternative to the above, define a class method that returns closures as the ones above, given a "dictionary key path", e.g. "with.complex.sub", as argument:
/* say 'myDictionary' is some class property (initialized as in example above)
In same class, introduce the following method */
func dictClosure(dictKeyPath: String) -> ((String, String) -> ()) {
let arr = dictKeyPath.componentsSeparatedByString(".")
if arr.count == 3 {
return {
myDictionary[arr[0]]?[arr[1]]?[arr[2]]?[$0] = $1 }
}
else {
return {
_, _ in
print("This closure is invalid")
}
}
}
/* example usage */
var smaller3 = dictClosure("with.complex.sub")
smaller3("dictionary", "NewestValue")
smaller3 = dictClosure("with.complex.anothersub")
smaller3("dictionary", "AlsoNewValue")
print(myDictionary)
/* ["with": ["complex": ["anothersub": ["dictionary": "AlsoNewValue"],
"sub": ["dictionary": "NewestValue"]]]] */
The above assumes dictionary key paths of three levels ("one.two.three"), and yields a closure for accessing the dictionary on the fourth level.
Finally note that for all solutions above, calling the smaller closures will allow for adding new key-value pairs into the fourth level of the dictionary, not only mutating the value of existing pairs. E.g. the key typo smaller3("dcitionary", "NewValue") will add a key-value pair "dcitionary": "NewValue" into the fourth level dictionary. If you only want to allow mutating values for existing key, simply add ? after the inner-most key access in the smaller closures above:
/* smaller ... */
dict["with"]?["complex"]?["sub"]?[key]? = val
/* smaller2 ... */
myDictionary["with"]?["complex"]?["sub"]?[$0]? = $1
/* smaller3 ... */
myDictionary[arr[0]]?[arr[1]]?[arr[2]]?[$0]? = $1

Updating a nested value in an NSDictionary

I've initialized a dictionary of type [NSObject: AnyObject] so I can save it into NSUserDefaults.
Here's what it looks like:
var allMetadata: [NSObject: AnyObject] = [
String: [String: String]
// Example: "project30": ["deliverablepath": "hello"]
]
I give deliverablepath a value from the very beginning, and later on I want to update it. I've tried this:
allMetadata[arrayOfProjectIDs[index]]!["deliverablepath"]! = "goodbye"
But I get the error
Operand of postfix '!' should have optional type; type is '(NSObject,
AnyObject)'
I know about updateValue(), but it seems to overwrite adjacent keys in the first nested layer, so it's not working for me.
Any ideas?
Use question optional to avoid "let pyramid"
var allMetadata: [String: [String: String]] = ["a": ["b": "c"]]
allMetadata["a"]?["b"] = "z" // ok!
allMetadata["q"]?["b"] = "d" // nil
UPD:
If you want to cast directly, you should try this:
var allMetadata: [NSObject: AnyObject] = ["a": ["b": "c"]]
if var dict = allMetadata["a"] as? [String: String] {
dict["b"] = "z"
// for dict update, because it's value typed
allMetadata["a"] = dict
}
Mention, that I've written "var", not "let" in condition.
To do this in a safe way, it is best to do this in an if let pyramid as follows:
if let projectId = arrayOfProjectIDs[index] {
if var project = allMetadata[projectId] as? [String:String] {
project["deliverablePath"] = "Goodbye"
}
}
That is not too bad actually.
I want to give an alternative answer here.
I understand the original question is about how to deal with nested arrays and dictionaries, but I think it is worth mentioning that this kind of data model may be better implemented with a more formal API.
For example, how about this:
class Project {
var id: String
var deliverablePath: String
... etc ...
}
class ProjectRepository {
func getProjectWithId(id: String) -> Project? {
...
}
}
Then you can use high level code like:
if let project = repository.getProjectWithId("") {
project.deliverablePath = "Goodbye"
}
Underneath you can still implement this with dictionaries and arrays of course.