Swift 4 - using a variable to select dictionary - swift

I have a number of dictionarys in my swift code that have a standard naming convention. What I am trying to do is programmatically selection which dictionary to extract the data from. As as example
var listSelectionValue = "aaaa"
let aaaalist : [Int : String ] = [1: "first value in aaaaa", 2 : "second value in aaaa"]
let bbbblist : [Int : String ] =[1: "first value in bbbb", 2 : "second value in bbbb"]
I then want to use the value in listSelectionValue for pull data from the correct dictionary. Sorry if this is exceedingly obvious, maybe I don't know right terminology to search for !!
Cheers,
Cameron

An if then else question?
var listSelectionValue = "aaaa"
let aaaalist = [Int : String ]()
if listSelectionValue == "aaaa"{
aaaalist : [Int : String ] = [1: "first value in aaaaa", 2 : "second value in aaaa"]
}
else{
let bbbblist : [Int : String ] =[1: "first value in bbbb", 2 : "second value in bbbb"]
}

Related

Structuring Firebase model for tableview sections

I have a data structure in firebase which I am showing that data on a tableview. I am getting the data from firebase. The tableview sections are hard coded like Motivation, Success etc... What is the best way to parse this data so that, when I add a new section in firebase console, it will add that section and data for the section on the tableview, without me hard coding the sections? Any help in the right direction would be appreciated, read the firebase doc but can't seem to figure it out.
Data Structure
{
"categories" : {
"motivation" : {
"one" : {
"name" : "Bob",
"title" : "Get up stand up"
},
"two" : {
"name" : "Arsitotle",
"title" : "Great philosopher"
}
},
"success" : {
"one" : {
"name" : "Les",
"title" : "You're great"
},
"three" : {
"name" : "Bob",
"title" : "One love"
},
"two" : {
"name" : "Wayne",
"title" : "You will be great"
}
}
}
}
** Retrieving the data**
ref.child("categories/motivation").observe(.childAdded, with: {(snapshot:DataSnapshot) in
if let values = snapshot.value as? [String:String] {
self.motivationDictionary.insert(values, at: 0)
}
})
}
ref.child("categories/success").observe(.childAdded, with: {(snapshot:DataSnapshot) in
if let values = snapshot.value as? [String:String] {
self.successDictionary.insert(values, at: 0)
}
})
}
I know this isn't the best way, but it works. Kind of redundant, but I am new to firebase and databases.
So you are having a fixed number of dictionaries where each dictionary has a name (motivationDictionary, successDictionary).
Instead you could have a dictionary of dictionaries (like in your data structure), such that the top dictionary contains categories, and under each key you have a dictionary of values for that category, i.e. the new self.categories["motivation"] is the same as the old self.motivationDictionary and so on.
This should work, but it is not the best practice to just operate on raw dictionaries and strings. This approach might be typical for some other languages (like Lisps), but not the way to go for Swift.
In Swift you should define your model classes, and parse your DataSnapshot as instances of those classes. For example if you start from:
struct Item {
let name: String
let title: String
}
class Category {
let name: String = ""
var items: [String: Item] = [:]
}
class TableDataModel {
var sections: [Category] = []
}
Then inside your observe, you can fill your TableDataModel, and then reload the table from the model. This way the Swift compiler helps you more to ensure that your program is correct, and the code is somewhat clearer.

NSTextField to Label by using TextField as the name of an dictionary

Swift4, Xcode9.3, Cocoa App.
How to change the string of labelMain to a dictionary word,
with a string key?
for example, a user put in "dict1" in the TextField,
the app should recognized that it is in the dict1 dictionary key "2",
and the label should print out "word2", instead of other words.
let dict0 : Dictionary<String, String> = ["0" : "word0", "1" : "word1"]
let dict1 : Dictionary<String, String> = ["2" : "word2", "3" : "word3"]
labelMain.stringValue = TextField.stringValue["2"]
error: Cannot subscript a value of type 'String' with an index of type 'String'
Create a dictionary that maps dictionary names to the actual dictionary:
let dict0 : Dictionary<String, String> = ["0" : "word0", "1" : "word1"]
let dict1 : Dictionary<String, String> = ["2" : "word2", "3" : "word3"]
let dictMap = ["dict0": dict0, "dict1": dict1]
if let dict = dictMap[TextField.stringValue], let word = dict["2"] {
labelMain.stringValue = word
}
or using optional chaining:
if let word = dictMap[TextField.stringValue]?["2"] {
labelMain.stringValue = word
}
or combined with the nil coalescing operator to provide a default value if none was found:
labelMain.stringValue = dictMap[TextField.stringValue]?["2"] ?? ""

Dictionary reference not updating dictionary

I have a reference to a dictionary and when I debug the code I can see that the reference is being populated but the actual dictionary is not getting any of the values.
var newItems = [String:Dictionary<String,AnyObject>]()
for item in self.items{
let itemRef = ref.child("items").childByAutoId()
//let itemOptions: NSMutableArray = []
//let itemRemovedOptions: NSMutableArray = []
//let itemDiscounts: NSMutableArray = []
newItems.updateValue([String:AnyObject](), forKey: "\(itemRef.key)")
newItems["\(itemRef.key)"]?.updateValue(item.parentCategory.id, forKey: "parentCategoryId")
newItems["\(itemRef.key)"]?.updateValue(item.item.id, forKey: "itemId")
newItems["\(itemRef.key)"]?.updateValue(item.item.name, forKey: "name")
newItems["\(itemRef.key)"]?.updateValue(item.qty, forKey: "qty")
newItems["\(itemRef.key)"]?.updateValue(item.discountAmount, forKey: "discountAmount")
newItems["\(itemRef.key)"]?.updateValue(item.notes, forKey: "notes")
newItems["\(itemRef.key)"]?.updateValue(item.price, forKey: "price")
//set options
newItems["\(itemRef.key)"]?.updateValue([String:Dictionary<String,AnyObject>](), forKey: "options")
for option in item.options{
if var optionsRef = newItems["\(itemRef.key)"]?["options"] as? [String:Dictionary<String,AnyObject>]{
optionsRef.updateValue([String:AnyObject](), forKey: "\(option.item.id)")
optionsRef["\(option.item.id)"]?.updateValue(option.item.name, forKey: "name")
optionsRef["\(option.item.id)"]?.updateValue(option.group.id, forKey: "group")
optionsRef["\(option.item.id)"]?.updateValue(option.qty, forKey: "qty")
optionsRef["\(option.item.id)"]?.updateValue(option.price, forKey: "price")
}
}
}
The primary issue here is that when you say if var optionsRef = newItems["\(itemRef.key)"]?["options"] as? [String:Dictionary<String,AnyObject>], you're making a copy of the Dictionary, since Dictionaries are value types in Swift. As a result, all subsequent modifications of the dictionary are applied only to your local copy, and not the one you intended. To alleviate this, you must assign the modified version of the dictionary back into the data structure where you wanted it changed.
But in addition to that primary problem, there are many other, softer, issues with this code. There's a reason this question over 50 views and no answers. People see this, say "Nope!" and run away screaming. Here are some key points:
Be consistent with your use of shorthand notation. [String : [String : AnyObject], not [String : Dictionary<String, AnyObject>].
Don't use string interpolation ("\(foo)") solely to convert types to strings. If the values are already strings, use them directly. If they're not, convert them with String(foo).
Don't use updateValue(_:forKey:). There's pretty much never a reason. Prefer subscript syntax.
When initializing a complex structure (such as your dictionary), initialize it first, then add it. Don't add it, then repeatedly access it through cumbersome copy/pasting.
In your case, consider newItems["\(itemRef.key)"]?.updateValue(.... With every line, the key must be hashed, and used to retrieve the value from the newItems dictionary. Then, the returned value must be checked for nil (by the ?. operator), doing nothing if nil, otherwise executing a method. You know the value won't be nil because you set it, but it has to do all this checking anyway.
Prefer Dictionary literals over mutation wherever possible.
Don't use Foundation data structures (NSArray, NSDictionary, etc.) it's absolutely necessary.
Here's my take on this code. It might need some touch ups, but the idea is there:
var newItems = [String : [String : AnyObject]() //fixed inconsistent application of shorthand syntax
let itemRef = ref.child("items").childByAutoId() //this shouldn't be in the loop if it doesn't change between iterations
for item in self.items {
// There's pretty much no reason to ever use updateValue(_:forKey:), when you can just use
// subscript syntax. In this case, a Dictionary literal is more appropriate.
var optionsDict = [String : [String : AnyObject]();
for option in item.options {
optionsDict[option.item.id] = [
"name" : option.item.name,
"group" : option.group.id,
"qty" : option.qty,
"price" : option.price,
]
}
newItems[itemRef.key] = [
"parentCategoryId" : item.parentCategory.id,
"itemId" : item.item.id,
"name" : item.item.name,
"qty" : item.qty,
"discountAmount" : item.discountAmount,
"notes" : item.notes,
"price" : item.price,
"options" : optionsDict
]
}
Here's an alternative that avoids having to define optionsDict separately:
let itemRef = ref.child("items").childByAutoId()
for item in self.items {
newItems[itemRef.key] = [
"parentCategoryId" : item.parentCategory.id,
"itemId" : item.item.id,
"name" : item.item.name,
"qty" : item.qty,
"discountAmount" : item.discountAmount,
"notes" : item.notes,
"price" : item.price,
"options" : item.options.reduce([String : AnyObject]()) { (var dict, option) in
optionsDict[option.item.id] = [
"name" : option.item.name,
"group" : option.group.id,
"qty" : option.qty,
"price" : option.price,
]
}
]
}

Cannot convert value of type [String : String?] to expected argument type [String : String]

I am trying to save data to my plist file which contains definition of array of strings. My plist -enter image description here
My code for writing the data to my plist is --
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true) as NSArray
let documentsDirectory = paths.objectAtIndex(0) as! NSString
let path= documentsDirectory.stringByAppendingPathComponent("authors.plist")
let fileManager = NSFileManager.defaultManager( )
var plistArray = NSArray(contentsOfFile: path) as! [Dictionary<String , String>]
var addItem: Dictionary = ["link": linkBook.text , "Cover": cover.text , "title" : titlePage.text , "year" : year.text , "isbn" : isbn.text]
plistArray.append(addItem)
// I am getting the error here
(plistArray as NSArray).writeToFile(path, atomically: true)
My guess is that linkBook, cover, titlePage, year and isbn all represent text fields in your UI? The text property of one or more of whatever those things has a type of Optional String (String?). The plistArray variable is of type [String, String] – neither the keys nor the values can be nil.
Fundamentally you need to answer the question of what to do if the text property of one of the things mentioned above is nil. It could be the case that they are never empty in which case you could add exclamation points to them linkBook.text! and cover.text! etc.
If you do that, however, and any of them ever is nil, that will lead to a crash.
I suspect instead that what you want to do is use the ?? operator. Set up your dictionary as:
var addItem: Dictionary = ["link": linkBook.text ?? "",
"Cover": cover.text ?? "",
"title" : titlePage.text ?? "",
"year" : year.text ?? "",
"isbn" : isbn.text ?? ""]
That way if any of the ever is nil then you will get the empty string in your plist.
The textfield.text is an optional. So you should unwrap it first.
var addItem: Dictionary = ["link": linkBook.text! , "Cover": cover.text! , "title" : titlePage.text! , "year" : year.text! , "isbn" : isbn.text!]

Swift: '(String) -> AnyObject?' does not have a member named 'subscript'

I am trying to convert a PFUser (from Parse.com) to a dictionary of [String : AnyObject] type, but I got the following error that seems very confusing to me. I did google search and found the following two SO questions, which, however, still not helpful in solving my problem:
Error:
'(String) -> AnyObject?' does not have a member named 'subscript'
Found these two posts:
relevant SO post 1,
relevant SO post 2
My PFUser has the keys: fullName, email, latitude, longitude, and linkedInUrl.
Since the types of the values for the above keys are a mix of String and Double. so I am trying to create the following Dictionary:
var userData : [String: AnyObject] = [
"fullName" : pfUser.valueForKey("fullName") as? String,
"latitude" : pfUser.valueForKey("latitude") as? Double,
"longitude" : pfUser.valueForKey("longitude") as? Double,
"email" : pfUser.valueForKey("email") as? String,
"linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String
]
The version of my XCode is 6.4. I appreciate your help.
You are declaring wrong type of userData. It must be [String: AnyObject?] because your values are optionals.
And don't forget to change this line "linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String with this line "linkedInUrl" : pfUser.valueForKey("linkedInUrl") as? String.
And your code will be:
var userData : [String: AnyObject?] = [
"fullName" : pfUser.valueForKey("fullName") as? String,
"latitude" : pfUser.valueForKey("latitude") as? Double,
"longitude" : pfUser.valueForKey("longitude") as? Double,
"email" : pfUser.valueForKey("email") as? String,
"linkedInUrl" : pfUser.valueForKey("linkedInUrl") as? String
]
There are two mistakes in your current code
First one is, use ( ) instead of using [ ] to get the value using the key in the following line.
"linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String
Second one is, use an exclamation point instead to unwrap the optional.
Your code should look like what's shown below.
var userData : [String: AnyObject] = [
"fullName" : pfUser.valueForKey("fullName") as! String,
"latitude" : pfUser.valueForKey("latitude") as! Double,
"longitude" : pfUser.valueForKey("longitude") as! Double,
"email" : pfUser.valueForKey("email") as! String,
"linkedInUrl" : pfUser.valueForKey("linkedInUrl") as! String
]
The other solution is, making the dictionary declaration as [String: AnyObject?] to hold the optional values.