Accessing Dictionary element for a tableview - ios

I have passed a dictionary to a second view controller and assigned it to an array, I thought I could access the data easier this way:
var myAlerts: NSDictionary!
The dictionary has three elements for each: Id (which I don't care about), alertDate, and alertNote.
I'm trying to get these elements into a tableView but struggling with this.
I thought about just moving it into two arrays and accessing it that way, cumbersome but it at least gets me further down the road so to speak.
Here is the raw data from the dictionary AFTER it was past to the second controller:
{
alerts = (
{
alertDate = "2017-07-16";
alertNote = "Rob is the worlds greatest friend";
id = 2;
},
{
alertDate = "2017-07-17";
alertNote = "This is a test of the emergency system";
id = 1;
}
);
}
When I tried to move the values into two arrays with this:
func CreateArray() {
for i in 0...myAlerts.count {
alertsDate[i] = myAlerts["alerts"]["alertDate"]
alertsNote[i] = myAlerts["alerts"]["alertNote"]
}
}
I get the proverbial Type Any? has no subscript members.
Any help would be appreciated.

myAlerts with that data is now a dictionary containing an array of dictionaries. (so top level is a dictionary, with one key/value pair which is of type array of [String:Any] objects).
Since a dictionary value is of type Any, it can't infer in this case what the type of the value for the key alerts is. So you have to try cast it to a specific type first, in this case an array of dictionaries, i.e. [[String:Any]]
So this should get rid of your error:
func CreateArray() {
for i in 0...myAlerts.count {
let alertArray = myAlerts["alerts"] as! [[String:Any]]
alertsDate.append(alertArray[i]["alertDate"] as! String)
alertsNote.append(alertArray[i]["alertNote"] as! String)
}
}
Note: I had to change alertsDate and alertsNote arrays to using append as in my demo code i had no existing items in the array and using and index would have caused an error.

Related

fill dictionary values in loop

I have a dictionary var items = [String:String]()
Then I assign values :
for i in itemInCart {
items["param_id"] = i.modelId
//items["param_id"]?.append(i.modelId!)
}
print(items)
I have just a last value.
I know that the method append() is not applicable to dictionaries, but how can I fill it then?
This is because you are replacing param_id every time for loop i so it will always a last element you will found
You have two options, either create a unique key for each element (a bad idea) or if you want all the elements you have to create array and add it into the param_id key like:
items["param_id"] = itemInCart.map { $0.modelId }
so now your dictionary is [String:Any]() or [String:[String]]()

I have and array inside the value of dictionary

I have an dictionary and the value containts the array of string as follows
arr = ["key":"["a","b","c","d","e","f","g"]"]
I want the new array to be like
let array = ["a","b","c","d","e","f","g"]
How to parse it
You can access dictionary items in few different ways, the easiest is:
let array = arr["key"]
You may need to conditionally unwrap it
if let array = arr["key"] as? [String] {
// rest of code with array
}

Ambiguous use of subscript?

I can make a Facebook SDK Graph Request to get a user's likes, but I'm having trouble taking the returned values and storing one of the keys in an array of Strings. The request returns an NSDictionary of keys/values. Then, using objectForKey I can get the data key which returns what I want: the id and name of the "liked" page on Facebook.
Data returns elements like this:
{
id = 486379781543416;
name = "Star Wars Movies";
},
I specifically want only the "name" of all of these objects and to throw them into an array [String]. I tried to loop through the objects but I'm getting error ambiguous use of subscript. Here's the relevant code:
request.startWithCompletionHandler{(connection:FBSDKGraphRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
let resultdict = result as! NSDictionary
let likes = resultdict.objectForKey("data") as! NSArray
print("Found \(likes.count) likes")
print(likes)
for object in likes{
let name = object["name"] as! String //error: ambiguous use of subsript
print(name)
}
}
After doing some research it looks like the issue is with the NSArray and that I should instead use Swift data types. I tried casting it to a Swift array but I got different errors.
What's the best way to handle this error?
Thanks!
update: Here is what the facebook API request returns:
{
data = (
{
id = 111276025563005;
name = "Star Wars (film)";
},
{
id = 115061321839188;
name = "Return of the Jedi";
}
);
paging = {
cursors = {
after = MTE1MDYxMzIxODM5MTg4;
before = Mjc0NzYzODk2MTg4NjY5;
};
next = "https://graph.facebook.com/v2.5/10155262562690368/likes?access_token=<redacted>";
};
}
You should always use the native Swift collection types wherever possible as NSArray and NSDictionary are really type-inspecific, and therefore can easily trigger "ambiguous use of subscript" errors.
You'll also want to avoid force down-casting, in case you receive data that's in the wrong format, or no data at all. This situation would be more elegantly handled with a guard, in order to prevent a crash. If your program depends on the force down-casting succeeding, and therefore should crash – then you can always call fatalError in the guard, with a descriptive error message in order to assist you in debugging the problem.
If I understand your data structure correctly, the request returns an AnyObject that should be a [String:AnyObject] (A dictionary of strings to any objects). In the case of the "data" key, the AnyObject value is then a [[String:AnyObject]] (An array of dictionaries of strings to any objects).
Therefore you'll want to do your casting in two stages. First, using a conditional downcast on your result to cast it as a [String:AnyObject]. If this fails, then the else clause of the guard will be executed and the code will return. You'll then want to get out your "data" value (your 'likes' array), and conditionally downcast it to a [[String:AnyObject]]. Both of these statements will handle the possibility of resultDict or resultDict["data"] being nil.
guard let resultDict = result as? [String:AnyObject] else {return}
guard let likes = resultDict["data"] as? [[String:AnyObject]] else {return}
You can put whatever error handling logic you want in the brackets of these statements to handle cases in which the results dictionary doesn't exist, was the wrong format, or there wasn't a 'likes' array in it.
You can then get an array of 'like' names through using flatMap.
let likeNames = likes.flatMap{$0["name"] as? String}
This will create an array of the like names of each dictionary – if the like names don't exist or aren't strings, then they won't be added. Because the compiler knows for certain that likes is a [[String:AnyObject]] – there's no ambiguity in subscripting its elements.
If you want a more general approach such as you're doing in your question, you can use a guard statement within a for loop.
for object in likes {
guard let name = object["name"] as? String else {continue}
print(name)
}
Again, you can put whatever error handling you wish in the brackets of the guard.

iOS Dictionary with two values per key?

I currently have a dictionary that contains 1 value per key. Is it possible to have 2 values per key?
var data = HomeVC.getData()
MyVariables.users = data.users;
MyVariables.img = data.img;
//MY Current dictionary with 1 value and 1 key
for (index, element) in enumerate(MyVariables.users)
{
MyVariables.dictionary[element as! String] = MyVariables.img[index]
}
I'm trying to add values from another array to this dictionary. So in total I would have 3 arrays in the same index position when calling them. 2 values and 1 key
You can use arrays or tuples as values of a dictionary:
var dictionary: Dictionary<String, (ValType1, ValType2)>
dictionary["foo"] = (bar, baz)
println(dictionary["foo"][1])
Instead of that try saving an NSArray as the value for key. The NSArray will be able to save more than 1 values and should suffice.
The lifecycle for a 2 values per key would look something like this
myDict[#"key1"] = value1;
// then set next value
myDict[#"key1"] = value2;
Now your dictionary has lost the first value and you only have access to the last one.
Your code would look something like
for (index, element) in enumerate(MyVariables.users)
{
var savedArray: [Your-Object-Type-Here]? = []
var savedArray = MyVariables.dictionary[element as! String]
if savedArray != nil {
savedArray!.append(MyVariables.img[index])
}
else {
savedArray = []
}
MyVariables.dictionary[element as! String] = savedArray
}
If your values are different types, create a custom class to hold the values. Make your dictionary keys point to objects of your custom class.

Error trying to access members of Array of AnyObject within a Dictionary - Is there a way around unwrapping?

I have a dictionary set up as:
var jDict = Dictionary<String, AnyObject[]>()
Where the arrays are either a collection of custom buttons (JunkButton) or Labels (JunkLabels).
I am having an issue when trying to access the members of the arrays contained in the Dictionary as follows:
let thisArray = jDict[key]
var aButton = thisArray[0] //Gives error: 'AnyObject[]? does not have a member named 'subscript'
I can get around this by downcasting the whole array as follows:
if let aArray = thisArray as? JunkButton[]{
var aButton = aArray[0]
}
This seems very cumbersome especially if I am sure I know what type the array is made up of beforehand. Is there a way to cast thisArray when it is created that would allow me to extract its elements without unwrapping them each time?
Dictionary always give you Optional value.
Your code is like this
let thisArray : Optional<AnyObject[]> = jDict[key]
You need to unwrap it to get non-optional value
let thisArray = jDict[key]! // thisArray is AnyObject[]
You really shouldn't use a dictionary for this. Swift makes it very easy to use custom little structs or classes instead of dictionaries:
struct JunkItems {
var buttons: [JunkButton] = []
var labels: [JunkLabel] = []
}
Then you can access those items like this without downcasting:
for button in junkItems.buttons {
// ...
}
Or:
if let button = junkItems.buttons[0] {
// ...
}
Btw, the array notation [JunkButton] is new in beta 3.

Resources