I am trying to convert JSON from an API into an array of cgpoints that I can use in core graphics.
The JSON looks like:
{"date":"2017-01-05","open":119.34,"high":120.2,"low":119.1,"close":119.7,"adjusted_close":109.7062,"volume":4261070},{"date":"2017-01-06","open":118.93,"high":121.5,"low":118.52,"close":120.76,"adjusted_close":110.6777,"volume":4089186},{"date":"2017-01-09","open":120.76,"high":121.06,"low":120.33,"close":120.43,"adjusted_close":110.3754,"volume":3021844}
I am interested in the date and close fields and I'd like to convert them into this format
dataPoints:@[xy(val0,119.7),xy(val1,120.76),xy(val2,120.43)];
where val0...val3 are dates mapped to the x dimension of the graph and I have defined:
#define xy(__x,__y) [NSValue valueWithCGPoint:CGPointMake(__x,__y)]
I am not wedded to the #define. It's just the code that CoreGraphics is using now. If there is another way to do it a struct or something that would be great.
I know how to decode the JSON into an array using:
struct StockReport: Codable {
let date: String
let stockDatumOpen, high, low, close: Double
let adjustedClose: Double
let volume: Int
enum CodingKeys: String, CodingKey {
case date
case stockDatumOpen = "open"
case high, low, close
case adjustedClose = "adjusted_close"
case volume
}
}
typealias StockData = [StockReport]
let stockData = try? newJSONDecoder().decode(StockData.self, from: jsonData)
Beyond that, however, I'm not sure how to create into an array of CGpoints.
I imagine I can convert the date strings into seconds since 1970 (epoch) and then scale those to the coordinate system. Or I can use the index of the array as an integer corresponding to each day but that's as far as I've gotten. Should I loop through the array stockData and create points one by one? Is there a more elegant way?
Thanks in advance for any suggestions.
Assuming you are receiving a valid json string (the string you have posted it is not a valid json string. You can sort your objects by its date (if they are not already sorted), enumerate the elements and initialize a CGPoint as follow:
let json = """
[{"date":"2017-01-05",
"open":119.34,
"high":120.2,
"low":119.1,
"close":119.7,
"adjusted_close":109.7062,
"volume":4261070},
{"date":"2017-01-06",
"open":118.93,
"high":121.5,
"low":118.52,
"close":120.76,
"adjusted_close":110.6777,
"volume":4089186},
{"date":"2017-01-09",
"open":120.76,
"high":121.06,
"low":120.33,
"close":120.43,
"adjusted_close":110.3754,
"volume":3021844}]
"""
do {
let stocks = try JSONDecoder().decode([StockReport].self, from: Data(json.utf8))
let points = stocks.sorted{$0.date < $1.date}.enumerated().map {
CGPoint(x: Double($0.offset), y: $0.element.close)
}
print(points)
} catch {
print(error)
}
This will print
[(0.0, 119.7), (1.0, 120.76), (2.0, 120.43)]