Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to convert dictionary of object_id : object to array of objects? #46

Open
pzmudzinski opened this issue Jun 13, 2018 · 4 comments
Open

Comments

@pzmudzinski
Copy link

Let's say we have structure like this:

     {
        "id1": {
            "name":"name1",
            "connections": {
                "id1": 1,
                "id2": 1,
            }
        },
        "id2": {
            "name":"name2",
            "connections": {
                "id1": 1,
                "id2": 0,
            }
        }
    }

I want to map it to structure like this:

struct MyData {
 let name: String
 let id: String # "id1", "id2" ...
 let connections: [String]
}

As you see id is on upper level while name and connections are on lower level.
Should I manually convert that dictionary to array before every parsing?
Can I somehow setup "pre-parser" which in case of converting Dictionary to Array[MyData] will flatten from Dictionary to Array[Dictionary]?

Also as side question.. since snapshot returns Dictionary[String: Any] it seems you can decode it using regular default JSONDecoder (or can't you?) - what's the advantage of using this library? Probably I am missing a point here :)

@luca251117
Copy link

luca251117 commented Jun 13, 2018

Here is an example for Cloud Firestore:

Fetch Collection of "MyData"
I consider your id1, id2 etc. are documents inside a collection. You can fetch all documents and map them into an array of MyData like this:

reference.getDocuments { (querySnapshot, error) in
            if let error = error {
                print(error.localizedDescription)
            } else {
                if let documents = querySnapshot?.documents {
                    let myDataArray = documents.map({ (snapshot) -> MyData in
                        let myData = try! FirestoreDecoder().decode(MyData.self, from: snapshot.data())
                        return myData
                    })
                    completion(myDataArray) // This is your array of MyData objects
                }
            }
        }

Another Example
Here is a code example for storing an array of MyData inside a collection.

       let batch = Firestore.firestore().batch()
        
        for myData in arrayOfMyData {
            let reference = db.collection("myData").document()
            let myDataDictionary = try! FirestoreEncoder().encode(myData)
            batch.setData(myDataDictionary, forDocument: reference)
        }
        
        batch.commit { (error) in
            if let error = error {
                print(error.localizedDescription)
            } else {
                completion("Upload successful")
            }
        }

There are two problems in your code:

1. Your myData.id is not part of the JSON Object itself. I would put the documentID into the JSON object like this:

{
        "id1": {
            "documentID: String #id1"
            "name":"name1",
            "connections": {
                "id1": 1,
                "id2": 1,
            }
        },
        "id2": {
            "documentID: String #id2"
            "name":"name2",
            "connections": {
                "id1": 1,
                "id2": 0,
            }
        }
    }

This makes it easier for FirestoreDecoder to map the documentID to the MyData struct.

2. I would consider putting the connections inside subcollections. If you want to add id3 or id1000 to document1, you have to fetch document1 with all the connections inside an array and add id1000 to it. This can cause problems in scalability the more connections you have inside one document.

Why CodableFirestore / CodableFirebase

Consider you have a struct like this

struct Person: Codable {
    var name: Name? // Firstname, Lastname, Salutation
    var address: Address? // Street, PostalCode, Country etc.
    var contactDetails: ContactDetails? // Phone, Mobile, etc.
    var foodPreferences: FoodPreferences? // ...
    ....
    ....
    
}

To encode for firebase / firestore or decode from it, you have to write a lot of boilerplate code. With CodableFirestore / CodableFirebase you can avoid encoding / decoding boilerplate code. That is why I am using it.

@pzmudzinski
Copy link
Author

Sorry, I should make it clear - I am using Firebase Realtime Database, not Firestore.

@luca251117
Copy link

Ok someone else can get this up for you, I never used FRD. Regardless of the data structure, encoding and decoding with CodableFirebase works identical for FRD, so maybe my post can help you to implement it for FRD.

@pzmudzinski
Copy link
Author

For now I've just created method for converting dictionary of keys:objects to array of objects:

extension Dictionary {
    func toArrayOfDictionaries() -> [Dictionary<String, Any>] {
        return self.map {
            let key = $0
            var value = $1 as! Dictionary<String, Any>
            
            value["id"] = key
            return value
            }.filter { $0["id"] != nil }
    }
}

Then use it like that:

{ [weak self] snapshot in
                guard let value = snapshot.value as? [String: Any] else {
                    return []
                }
                
            let neighborhoods = value.toArrayOfDictionaries()
            return try self?.decoder.decode([Neighborhood].self, from: neighborhoods) ?? []
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants