how to make a swift dictionary that holds two items that are array of other items - swift

I'd like to create a dictionary that holds two arrays (one key is called "locations" and the other is "items") like this:
var tmpResults = Dictionary("locations",Array)
tmpResults["items"]=Array
or something like this (neither of which seem to work):
var tmpResults = ["locations",:<Location>,"items":<Item>]
var tmpResults = ["locations":Array(Location),"items":Array(Item)]
but I'm really not sure how to do this in Swift. How would I specify the types that the arrays could hold?

I think the easiest solution would be AnyObject.
var tmpResults: [String: AnyObject] = [:]
tmpResults["locations"] = [location1, location2, location3]
tmpResults["items"] = [item1, item2, item3]
// Assuming location1, location2, and location3 are all of the same "Location" type
// And item1, item2, and item3 are all of the same "Item" type
By using AnyObject, your dictionaries objects could be almost anything. In this example, you have a dictionary that holds and array of Locations at one key, and an array of Items at another key.
You do lose some of the nice type-checking Swift does though.
Edit
In fact, you could declare it like this, so that the compiler at least knows your dictionary holds arrays:
var tmpResults: [String: [AnyObject]] = [:]
In either case, you use the array you'd probably do something like this:
if let items = tmpResults["items"] as? [Item] {
// Do something with you items here
}

In order to hold array in dictionary, you can go with the following code:
var tempResults : Dictionary<String, Array<String>>
This can be reduced to:
var tempResults : [String: [String]]
Then you can add your Array items to it:
var array1 : [String] = [String]()
array1.append("location1")
array1.append("location2")
array1.append("location3")
var array2 : [String] = [String]()
array2.append("item1")
array2.append("item2")
array2.append("item3")
And finally, you can add it to your Dictionary
tempResults["location"] = array1
tempResults["items"] = array2
Hope this helps!

Related

What Data Structure should I use for this particular case?

I have a dictionary, something like this
var dict = [String : [String]]()
The functionality I want to achieve is that, I have a hashtable which I can quick get the list of data from.
In my code, I use a dictionary and an array.
I am not very good with algorithem and data structure, so I am wondering if there is any better data structure that is suitable for something like this?
Use:
var dict = [String : [String]]()
Swift already has built in search algorithms that allow you you to retrieve data inside of your dictionary with simple subscript syntax like so
dict["element"]
You will use it in this way -
Declaration:
var dict: [String: [String]] = [:]
Initialise:
dict["element"] = myArray

Immutable value error when appending to array within dictionary after downcasting

var someDict = [String:Any]()
someDict["foo"] = ["hello"]
(someDict["foo"] as? [String])?.append("goodbye") // error here
I am trying to add a value to an existing dictionary containing an array. The dictionary also contains other non-array values, so it has to have value type Any. The problem is that, when I do this, I get an error Cannot use mutating member on immutable value of type '[String]'. Some Googling turned up a few references such as this suggesting that arrays within dictionaries are always immutable, but the compiler doesn't complain if I do this:
var someDict = [String:[String]]()
someDict["foo"] = ["hello"]
someDict["foo"]?.append("goodbye")
so I suspect that information is outdated and it's something specific to the downcasting. Is there any way I can get around this without copying and re-assigning the entire dictionary value?
Yes, it is related the the downcasting. Try this instead:
var someDict = [String:Any]()
someDict["foo"] = ["hello"]
if var arr = someDict["foo"] as? [String] {
arr.append("goodbye")
someDict["foo"] = arr
}

Create a dictionary in a loop in Swift

I have a variable set of objects that I need to place in a dictionary. I'm trying to add them to the dictionary in a for loop but from what I'm understanding dictionaries are immutable so they need to be declared immediately. How do I create a dictionary list of items that are not predetermined?
var newItems = [:]
for item in self.items{
newItems["\(item.key)"]["name"] = "A new item"
}
does not use the second value
var newItems : [String:String] = [:]
for i in 1..10{
newItems[i.description] = "A new item"
}
for more information https://www.weheartswift.com/dictionaries/
The problem with your original code is that dictionaries only have one key, so this construct newItems["\(item.key)"]["name"] is syntactically incorrect. If you had a fixed number of properties you could use a struct and put that in a dictionary. As you posed the question, though, you need to create a dictionary where the stored elements themselves are dictionaries. So, although I didn't actually put this into Xcode, it's a template for what you need to do:
var newItems = [:[:]]()
for item in self.items {
var itemDict = [:]()
for prop in whereeveryourpropertiescomefrom {
itemDict[prop] = valueforthisproperty
}
newItems["\(item.key)"] = itemDict
}
Of course, if your properties were initially stored in a dictionary unique to this item (equivalent of the inner loop), just store it directly into newItems.
Then, you could reference things as
let value = newItems["\(item.key)"]?.["property key"]
Notice the dictionary retrieval returns an optional you have to deal with.
The solution was when initializing the dictionary to create another dictionary
var newItems: [String:[String:AnyObject]]()

Append element to existing key in a dictionary? [duplicate]

This question already has answers here:
swift: modifying arrays inside dictionaries
(5 answers)
Closed 7 years ago.
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
// initialize the wordDict
wordDict[0] = iniArray
Now let's say I want to add another string element to the value of the key 0. For example "bike". So that it becomes:
wordDict[0] = ["yikes", "bye", "bike"]
How can I do this?
It looks like this part of your code:
var wordDict: [Int: Array<String>]?
Is unintentional. Here, you're making the whole dictionary optional, which doesn't make much sense. This is what (I'd imagine) you intended:
var wordDict: [Int: Array<String>]
It's preferred to use the array shorthand, generally, though:
var wordDict: [Int: [String]]
anyway, to initialise this dictionary as an empty dictionary, you need to do this:
var wordDict: [Int: [String]] = [:]
Then, to add the initial array, you do this:
wordDict[0] = ["yikes", "bye"]
And finally, to append the extra element, you use optional chaining:
wordDict[0]?.append("bike")
If you did intend for the entire dictionary to be optional, you just need an extra ?:
var wordDict: [Int: [String]]? = [:]
wordDict?[0] = ["yikes", "bye"]
wordDict?[0]?.append("bike")
wordDict?[0] // ["yikes", "bye", "bike"]
First, it's worth pointing out that there are two problems in your code:
Trying to use wordDict before it is initialized
Trying to use wordDict as if it were not an optional
Because you made wordDict optional, you have to always ensure it is not nil before you use it. Right now, in your code snippet, wordDict is nil.
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
wordDict[0] = iniArray // error: value of optional type '[Int : Array<String>]?' not unwrapped; did you mean to use '!' or '?'?
The later problem can be solved by using ?:
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
wordDict?[0] = iniArray
wordDict?[0] = ["yikes", "bye", "bike"]
But if that's the only change you make, then you might be surprised about the value:
print(wordDict) // prints "nil"
This is because you never actually initialized your dictionary before trying to put a value (the array) into it. The syntax wordDict? can be thought of as saying "if wordDict is not nil, then proceed with the rest of the expression". Because it wasn't initialized, the putting of the key into the dictionary never happened. The following code works:
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]? = [:] // now `wordDict` is initialized, but still an optional
wordDict?[0] = iniArray
wordDict?[0] = ["yikes", "bye", "bike"]
The second line, were the dictionary is initialized, can be written in several ways, these are all the same:
var wordDict: Dictionary<[String]>? = [:]
var wordDict: Dictionary<Int, [String]>? = [:]
var wordDict: Dictionary<Int, [String]>? = Dictionary()
var wordDict: [Int: [String]]? = [:]
var wordDict: [Int: [String]]? = Dictionary()
In all the above cases, you have initialized an optional, empty dictionary. Any time you try to use that dictionary it needs to be conditionally unwrapped (using ?) or force unwrapped (using !, but it is discouraged to force-unwrap).
Also, if you want to append to the array for that key instead of replacing it completely:
wordDict?[0]?.append("foo")
But these are all value types, so I'm pretty sure this is still replacing the array in wordDict.
You can use the append method on the array class
wordDict.append("bike")
see ref
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html

(String: AnyObject) does not have a member named 'subscript'

I've been through similar questions but still do not understand why my code is throwing an error.
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"][0] = "baz"
The error is on line 3: (String: AnyObject) does not have a member named 'subscript'
I'm setting the participants key to an array and then trying to update the first element of it without any luck. The code above is shortened for example purposes, but I am using [String:AnyObject] because it is not only arrays that are stored in the dictionary.
It's probably something really trivial but I am still new to Swift. Thanks for any help in advance!
The error message tells you exactly what the problem is. Your dictionary values are typed as AnyObject. I know you know that this value is a string array, but Swift does not know that; it knows only what you told it, that this is an AnyObject. But AnyObject can't be subscripted (in fact, you can't do much with it at all). If you want to use subscripting, you need to tell Swift that this is not an AnyObject but rather an Array of some sort (here, an array of String).
There is then a second problem, which is that dict["participants"] is not in fact even an AnyObject - it is an Optional wrapping an AnyObject. So you will have to unwrap it and cast it in order to subscript it.
There is then a third problem, which is that you can't mutate an array value inside a dictionary in place. You will have to extract the value, mutate it, and then replace it.
So, your entire code will look like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
var arr = dict["participants"] as [String] // unwrap the optional and cast
arr[0] = "baz" // now we can subscript!
dict["participants"] = arr // but now we have to write back into the dict
Extra for experts: If you want to be disgustingly cool and Swifty (and who doesn't??), you can perform the mutation and the assignment in one move by using a define-and-call anonymous function, like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"] = {
var arr = dict["participants"] as [String]
arr[0] = "baz"
return arr
}()