rish
rish

Reputation: 5

Order changed for the parsed response when casting the response into a dictionary

I am getting a response from the remote server from which i had created a jsonObject. now when I'm iterating the response for the key "instances" which contains a value of dictionary the response is shuffled. the key element of the dictionary which is a date is not coming in sequence while printing.

here is the json response:

{
    "data": [
        {
            "id": "apkqY19RthbN",
            "theater_id": "clzEVgztkcWB",
            "theater_audi_id": "7LFeNCaCrm8h",
            "movie_lang_id": "0VTEIjLeDxK1",
            "booking_start_date": null,
            "instances": {
                "2018-04-20": [
                    {
                        "id": "WRK81ahlWcZZ",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "cDqZjYKHP2xt",
                        "show_time": "20:00:00"
                    }
                ],
                "2018-04-21": [
                    {
                        "id": "DcVQhohv9C3W",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "bZOUk3AT6TMM",
                        "show_time": "20:00:00"
                    }
                ],
                "2018-04-22": [
                    {
                        "id": "5YJAydTua6b2",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "qKGXgWvV0r38",
                        "show_time": "20:00:00"
                    }
                ],
                "2018-04-23": [
                    {
                        "id": "AGciEXINppMe",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "YbKFBJV67hFW",
                        "show_time": "20:00:00"
                    }
                ],
                "2018-04-24": [
                    {
                        "id": "5vj9t1i5B4J3",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "um7UeNPJuAcv",
                        "show_time": "20:00:00"
                    }
                ],
                "2018-04-25": [
                    {
                        "id": "AbS69J4SM4gG",
                        "show_time": "17:00:00"
                    },
                    {
                        "id": "gKax9RXMLozN",
                        "show_time": "20:00:00"
                    }
                ]
            }
        }
    ]
}

here is my code that i had tried:

 _ = URLSession.shared.dataTask(with: request) { (Data, response, error) in
            if Data != nil{
                do{

                    let access = try JSONSerialization.jsonObject(with: Data!, options: []) as! [String: Any]
                   // print(access)
                    if let data = access["data"] as? [[String:Any]]{
                        if let id = data[0]["id"] as? String{
                            global.showID = id
                          //  print(data)
                        }
                        var j = 0
                        for l in 0..<data.count{
                        if let instances = data[l]["instances"] as? [String:Any] {
                            print(instances)
                            for (key,obj) in instances{
                                //for (key,obj) in (instances as [String:Any]){
                                //print(key)
                                var showDetails: [ShowData] = [ShowData]()
                                if let timeObj = obj as? [[String: Any]]{
                                    var k = 0
                                    for i in 0..<timeObj.count{
                                        let showTime = timeObj[i]["show_time"]
                                        //print(showTime!)

                                        let showID = timeObj[i]["id"]
                                        //print(showID!)

                                        let a = ShowData.init(showId: showID as! String, showtime: showTime as! String)
                                        showDetails.insert(a, at: k)
                                        //print(showDetails)
                                        k += 1
                                    }
                                }
                                let a = Shows(date: key , instance: showDetails)
                                self.showsDate?.insert(a, at: j)
                                //print(self.showsDate!)
                                j += 1
                            }
                            Completion(self.showsDate!)
                        }
                        }
                    }
                }catch let e{
                    print(e)
                }
            }
            }.resume()

print(instances) when i am printing this the output is :

["2018-04-25": <__NSArrayI 0x143bd1ac0>(
{
    id = AbS69J4SM4gG;
    "show_time" = "17:00:00";
},
{
    id = gKax9RXMLozN;
    "show_time" = "20:00:00";
}
)
, "2018-04-24": <__NSArrayI 0x143b3aca0>(
{
    id = 5vj9t1i5B4J3;
    "show_time" = "17:00:00";
},
{
    id = um7UeNPJuAcv;
    "show_time" = "20:00:00";
}
)
, "2018-04-23": <__NSArrayI 0x143be0ce0>(
{
    id = AGciEXINppMe;
    "show_time" = "17:00:00";
},
{
    id = YbKFBJV67hFW;
    "show_time" = "20:00:00";
}
)
, "2018-04-21": <__NSArrayI 0x143b349c0>(
{
    id = DcVQhohv9C3W;
    "show_time" = "17:00:00";
},
{
    id = bZOUk3AT6TMM;
    "show_time" = "20:00:00";
}
)
, "2018-04-22": <__NSArrayI 0x143b24290>(
{
    id = 5YJAydTua6b2;
    "show_time" = "17:00:00";
},
{
    id = qKGXgWvV0r38;
    "show_time" = "20:00:00";
}
)
, "2018-04-20": <__NSArrayI 0x143bd5430>(
{
    id = WRK81ahlWcZZ;
    "show_time" = "17:00:00";
},
{
    id = cDqZjYKHP2xt;
    "show_time" = "20:00:00";
}
)
]

the dates are not in sequence like in the response.

Upvotes: 0

Views: 184

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347244

Dictionary's don't maintain key order.

Let's just start with some basic data...

struct ShowData: CustomStringConvertible {
    var id: String
    var time: String
    
    var description: String {
        return "id = \(id); time = \(time)"
    }
}

var rawData: [String: [ShowData]] = [:]

rawData["2018-04-20"] = [
    ShowData(id: "WRK81ahlWcZZ", time: "17:00:00"),
    ShowData(id: "cDqZjYKHP2xt", time: "20:00:00")]
rawData["2018-04-21"] = [
    ShowData(id: "DcVQhohv9C3W", time: "17:00:00"),
    ShowData(id: "bZOUk3AT6TMM", time: "20:00:00")]
rawData["2018-04-22"] = [
    ShowData(id: "5YJAydTua6b2", time: "17:00:00"),
    ShowData(id: "qKGXgWvV0r38", time: "20:00:00")]
rawData["2018-04-23"] = [
    ShowData(id: "AGciEXINppMe", time: "17:00:00"),
    ShowData(id: "YbKFBJV67hFW", time: "20:00:00")]
rawData["2018-04-24"] = [
    ShowData(id: "5vj9t1i5B4J3", time: "17:00:00"),
    ShowData(id: "um7UeNPJuAcv", time: "20:00:00")]
rawData["2018-04-25"] = [
    ShowData(id: "AbS69J4SM4gG", time: "17:00:00"),
    ShowData(id: "AbS69J4SM4gG", time: "20:00:00")]

for key in rawData.keys {
    print(key)
}

For me, this prints out...

2018-04-25
2018-04-24
2018-04-23
2018-04-21
2018-04-20
2018-04-22

Now, you could look to finding a OrderedDictionary or you could Map the keys to an array and sort the array, which would give you vector back into the Dictionary

Or, you could sort the Entry data of the Dictionary...

let sorted = rawData.sorted { (lhs, rhs) -> Bool in
    return lhs.key < rhs.key
}

for entry in sorted {
    print(entry.key)
}

Which outputs...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

Now, what's important to remember here is, this is nothing more then a String comparison, but you're dealing with dates. Personally, I hate dealing with String date/time values and as soon as I can, I convert to something which is actually practical for there purpose...

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"

let actuallySortedByDate = rawData.sorted { (lhs, rhs) -> Bool in
    return formatter.date(from: lhs.key)! < formatter.date(from: rhs.key)!
}

for entry in sorted {
    print(entry.key)
}

Which outputs...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

While this might not look any different from the first sorting, the comparisons performed are more actual to the data type (date objects).

Important

This is a demonstration of an idea. It's very important to understand that it's possible for the key to be invalid and the Date conversion to fail. Personally, I'd prefer to have the rawData keyed by Date, rather the String as it allows for an earlier detection of the issue. I've used a "forced unwrap" in the example purely for demonstration - you should validate the keys before hand and ensure that data is correct

Caveat: Mapping the keys in a Dictionary is actually a lot harder then it would seem to need to be. You can have a look at What's the cleanest way of applying map() to a dictionary in Swift? for more details. Needs less to say, use Swift 4!

Okay, so what this attempts to do, is convert the original [String: ShowData] Dictionary to a [Date: ShowData] Dictionary in a way which would allow you to deal with the possibility of invalid String keys.

enum ParserError: Error {
    case invalidDateFormat
}

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"
do {
    let mapped = try Dictionary(uniqueKeysWithValues: rawData.map({ (entry) throws -> (Date, [ShowData]) in
        guard let date = formatter.date(from: entry.key) else {
            throw ParserError.invalidDateFormat
        }
        return (Calendar(identifier: .gregorian).startOfDay(for: date), entry.value)
    }))

    let actuallySortedByDate = mapped.sorted { (lhs, rhs) -> Bool in
        return lhs.key < rhs.key
    }
    
    for entry in actuallySortedByDate {
        print(entry.key)
    }
} catch let error {
    print(error)
}

And this eventually ends up printing:

2018-01-19 13:00:00 +0000
2018-01-20 13:00:00 +0000
2018-01-21 13:00:00 +0000
2018-01-22 13:00:00 +0000
2018-01-23 13:00:00 +0000
2018-01-24 13:00:00 +0000

ps: The idea is to try and take the time component out of the equation

Upvotes: 1

Paulw11
Paulw11

Reputation: 114875

Dictionaries are unordered. You will also find that dealing with variable keys (your dates) can be tricky too. You should make instances an array and put the date in a dictionary value.

If you do this then you can make a couple of structs that adopt Codable and reduce your JSON parsing down to 1 line

Upvotes: 0

Related Questions