Reputation: 5
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
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).
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
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 struct
s that adopt Codable
and reduce your JSON parsing down to 1 line
Upvotes: 0