2

I have a multidimensional dictionary and I am trying to add data into it without deleting data, but overwrite if duplicate.

var items = [Int: AnyObject]()

var IdsAndDetails = [String:AnyObject]()
let index = 3 // static for test

...
for result in results {
     // result is String (result also indicates itemId)

     let details = self.IdsAndDetails[result]  
     // details is AnyObject like [String:String]

    if let itemDetails = details {        
         // Here I want to append data into 'items' variable

         // This doesn't work: (Error-1)
          self.items[index]![result] = itemDetails

    }

(Error-1):

Cannot assign to immutable expression to type AnyObject.


However, if I try like, it works but it's not the approach I want. It's re-creating the dictionary. Instead, I want to append the data.

      self.items = [
         index : [result : itemDetails]
      ]

The structure of dictionary I want to get in the end is:

items = [
    index : [
        "id1" : ["key": "value", "key": "value"],
        "id2" : ["key": "val", "key": "val"],
        ],
    index : [
        "id3" : ["key": "val", "key": "val"],
        "id4" : ["key": "val", "key": "val"],
        "id5" : ["key": "val", "key": "val"],
    ]
]

// index is Integer
// [Key:Value] is [String:String] - itemDetails equal to all `[key:value]`s
// 'id' is also String

Update: I also tried, but no luck

     let a = self.items[index]
     a![result]! = itemDetails as [String:String]

Update 2:

    let valueDict = (value as! NSDictionary) as Dictionary

    for (key, val) in valueDict {
        let keyString = key as! String
        let valString = val as! String

        self.items[index]![result]![keyString]! = valString
    }

But it's throwing error:

fatal error: unexpectedly found nil while unwrapping an Optional value

But Surprisingly debugging shows all values:

po index : 1
po itemId : "123123"
po keyString: "keyInString"
po valString: "valInString"

Update 3:

 for index in 1...5 {
   var results = [String]()

   // First I retrieve nearby users and assign it to key

   let itemsRef = Firebase(url: self.secret + "/items")
   eventsRef.queryOrderedByChild("user_id").queryEqualToValue(key).observeEventType(.ChildAdded, withBlock: { snapshot in
   // ^ above 'key' is the user_id retrieved before

   let itemDetails = snapshot.value // item details - [key:val, key:val]
   let itemId = snapshot.key        // item ids [id1,id2,id3]

   // I used 'KeyAndDetails' to store all values with ids 
   let IdsAndDetails = [itemId: itemDetails]

   self.itemIdsArray = []
   self.itemIdsArray.append(itemId)

   if index == 1 {
       // self.items = [
       //    index : [itemId : itemDetails]
       // ]
       // ^ This worked and gave me the structure
       // but I don't want to overwrite it, instead, I want to append
       // on the dictionary

       // This is where I am trying to append into `self.items`,
       // and throws error:
       self.items[index]?[result] = (eventDetails as! [String : String])
   }
  ...
senty
  • 12,385
  • 28
  • 130
  • 260
  • Note: you should use an array instead of a dictionary with numbers as keys (at `var items = [Int: AnyObject]())`. It's the same idea: you access by index - but an array has better semantics for the task. – Eric Aya May 24 '16 at 22:08
  • Is that the reason of the problem? Can you please describe more? – senty May 24 '16 at 22:09
  • It's not a reason or a solution to your problem, it's an alternative. :) If you use an array instead of a dictionary, you will get the same features without the hassle of having to handle a dictionary. A tip: avoid using dictionaries extensively, prefer using objects when possible (classes, structs, etc) and store them in an array if you need an indexed collection. – Eric Aya May 24 '16 at 22:18
  • How can I declare a multidimensional array `var items = [Int: AnyObject]()`? I was thinking if I need to store something multidimensional, I need to use dictionary. What is the most appropriate way of storing in my case? Can you please please write an answer below? Is storing it as an object the best bet? After this process, I am going to display these values in TableView. – senty May 24 '16 at 22:23
  • Sure, I can give you the data kind I want to store. In the last bit of my question - 'structure', 'index' would be Integer, 'ids' are String, and 'key:value' are "String:String". The 'key:value' bit is equal to `itemDetails` I used in the above code. `ItemDetails` is [Key:Value] as [String:String]. Is that the part you were asking? – senty May 24 '16 at 22:37
  • @EricD Can you please please guide me on this? – senty May 24 '16 at 23:34
  • Is there a reason you're using `AnyObject` instead of `[String: String]` in your declarations? i.e. `var items = [Int: [String: String]]()` – Mike S May 25 '16 at 00:03
  • I tried `var items = [Int: [String: [String: String]]]()` and `self.items[index]![result] = (itemDetails as! [String:String])`, however it started giving a weird crash. 'Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)` – senty May 25 '16 at 00:11

1 Answers1

2

It seems like you're trying to bypass Swift's type system instead of working with it. Instead of using AnyObject, you should be trying to use the exact type you want for the value of that dictionary. In this case it looks like you want something like [Int: [String: [String: String]]] (although, like @EricD said in the comments, you should probably be using a struct instead, if at all possible).

Here's a quick (static) example similar to the code in your question:

var items = [Int: [String: [String: String]]]()
let idsAndDetails = ["id2": ["key3": "value3"]]

let index = 3
items[index] = ["id1": ["key1": "value1", "key2": "value2"]]

let result = "id2"
if let itemDetails = idsAndDetails[result] {
    items[index]?[result] = itemDetails
}

At the end of that, items will be:

[3: ["id1": ["key1": "value1", "key2": "value2"], "id2": ["key3": "value3"]]]

The ? in items[index]?[result] tells Swift to make sure items[index] is non-nil before attempting to execute the subscript method. That way, if you try to update an index that doesn't exist in items you don't cause a crash.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
Mike S
  • 41,895
  • 11
  • 89
  • 84
  • Please check my update 1 and update 2. It's crashing when I try casting `itemDetails as! [String:String]`. So it won't let me run `self.items[index]?[result] = (itemDetails as! [String : String])` – senty May 25 '16 at 00:50
  • Are you sure `itemDetails` is a `[String:String]`? If you use `as!` like that and it's _not_ a `[String:String]`, that's going to cause a crash. What exactly is in `IdsAndDetails`, or where does it come from? – Mike S May 25 '16 at 00:53
  • When I 'alt+click', it says it's AnyObject. However if I use itemDetails["key"], is returns the value in String :/ That's why I thought it's [String:String]. It's also [keys:values] are [String:String] in database – senty May 25 '16 at 00:56
  • If `itemDetails["key"]` is returning a `String`, then `itemDetails as! [String: String]` is definitely going to cause a crash. You said it's `[String: String]` in the database... what database? How is the data assigned to `IdsAndDetails`? Could you put more info about that in your question maybe, or include some relevant code? – Mike S May 25 '16 at 01:04
  • I use Firebase as backend. What I mean by `[String : String]` in database, I meant keys are String and value are also String. Should I write the whole chunk of code? – senty May 25 '16 at 01:06
  • The code might help. What I'm trying to figure out now is if the database is a key/value store where both are `String`s, and that's loaded directly in to `IdsAndDetails`, then how is `IdsAndDetails[result]` supposed to return a `[String:String]`? – Mike S May 25 '16 at 01:20
  • I updated the question and added my code for that part.. Hope it explains – senty May 25 '16 at 01:35
  • 1
    I honestly don't know much about Firebase, but it looks to me like `snapshot.value` is a `String` when you're expecting it to be a `[String: String]`. If that's true, that's probably either because Firebase doesn't support storing a non-`String` value, or that the data originally stored in the database was not that type you thought it was. If it's the former, you'll have to parse that `String` (maybe with a JSON parser). If it's the later, you might need to do something with the data at the database level. Either way though, that's probably a separate issue from your original question. – Mike S May 25 '16 at 02:01