问题
I am trying to append all closing prices from the most recent to the oldest available in a given JSON dictionary. The URL request that I'm using is the following:
https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=AMZN&interval=60min&outputsize=full&apikey=5H2L6THH6BSCAMKO
As you can see, the result is a dictionary ordered with the most recent dates and within that dictionary, there is various options, including the closing price. A little bit of it can be seen below.
{
"Meta Data": {
"1. Information": "Intraday (60min) open, high, low, close prices and volume",
"2. Symbol": "AMZN",
"3. Last Refreshed": "2018-08-15 12:30:00",
"4. Interval": "60min",
"5. Output Size": "Full size",
"6. Time Zone": "US/Eastern"
},
"Time Series (60min)": {
"2018-08-15 12:30:00": {
"1. open": "1892.1620",
"2. high": "1898.0000",
"3. low": "1879.3600",
"4. close": "1892.5000",
"5. volume": "893752"
},
"2018-08-15 11:30:00": {
"1. open": "1873.4399",
"2. high": "1893.3500",
"3. low": "1873.4399",
"4. close": "1892.1200",
"5. volume": "798959"
},
"2018-08-15 10:30:00": {
"1. open": "1905.1899",
"2. high": "1915.5300",
"3. low": "1871.0200",
"4. close": "1871.0200",
"5. volume": "1614045"
},
Now, I want to append, from the most recent date and below it in order, the closing price. I've tried to use a for loop, while loops and playing a bit with the Date structure to successfully request the closing price of each date in an orderly fashion, however it gets stuck when I try to access a former date. The function I am using is the following one.
func get1HPriceArr(stock:String, dataPoints:Int){
let url = "https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=\(stock)&interval=60min&outputsize=full&apikey=5H2L6THH6BSCAMKO"
Alamofire.request(url, method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let timeZone = TimeZone(secondsFromGMT: 0000)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = timeZone!
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let json = JSON(value)
var DatesArray:[String] = []
var ClosingPrices:[Double] = []
var followingDate:String?
func AddDates() -> [Double]{
print("FollowingDate just below the func is \(followingDate)")
while ClosingPrices.count != dataPoints {
print("Following date in the while loop is \(followingDate)")
let mostRecentRefreshed = json["Meta Data"]["3. Last Refreshed"].stringValue
print("Most recent refreshed is \(mostRecentRefreshed)")
if followingDate == nil {
followingDate = mostRecentRefreshed
let closingPrice = json["Time Series (60min)"][followingDate!]["4. close"].doubleValue
print("Following Date was nil and now its value is \(followingDate!)")
DatesArray.append(followingDate!)
ClosingPrices.append(closingPrice)
print(ClosingPrices)
// You must check if most recent refreshed is 16:00
let followingDateDate = dateFormatter.date(from: followingDate!)
let hour = Calendar.current.component(.hour, from: followingDateDate!)
let minutes = Calendar.current.component(.minute, from: followingDateDate!)
if hour == 16{
let halfNHourMore = Calendar.current.date(byAdding: .minute,value: 30, to: followingDateDate!)
let dateStringNil = dateFormatter.string(from: halfNHourMore!)
followingDate = dateStringNil
} else {
print("Last refreshed time is not 16:00, so following date was nil and now is \(followingDate)")
}
}
print("Following date is \(String(describing: followingDate))")
var currentDate = dateFormatter.date(from: followingDate!)
print("Current Date is \(currentDate)")
let dateHour = Calendar.current.component(.hour, from: currentDate!)
let dateMinutes = Calendar.current.component(.minute, from: currentDate!)
var oneHourLess:Date!
if dateHour == 16 && dateMinutes == 0 {
oneHourLess = Calendar.current.date(byAdding: .minute,value: -30, to: currentDate!)
} else {
oneHourLess = Calendar.current.date(byAdding: .hour,value: -1, to: currentDate!)
}
let dateString = dateFormatter.string(from: oneHourLess!)
if (dateString.contains("17:30:00")) || (dateString.contains("18:30:00")) || (dateString.contains("19:30:00")) || (dateString.contains("20:30:00")) || (dateString.contains("21:30:00")) || (dateString.contains("22:30:00")) || (dateString.contains("23:30:00")) || (dateString.contains("00:30:00")) || (dateString.contains("01:30:00")) || (dateString.contains("02:30:00")) || (dateString.contains("03:30:00")) || (dateString.contains("04:30:00")) || (dateString.contains("05:30:00")) || dateString.contains("06:30:00") || dateString.contains("05:30:)0") || dateString.contains("06:30:00") || dateString.contains("07:30:00") || dateString.contains("08:30:00") || dateString.contains("09:30:00") {
print("OneHourLess is \(oneHourLess)")
print("Its closing time since the time is \(dateString)")
} else {
DatesArray.append(dateString)
followingDate = dateString
print("Following date is \(String(describing: followingDate))")
currentDate = dateFormatter.date(from: followingDate!)
let closingPrice = json["Time Series (60min)"][followingDate!]["4. close"].doubleValue
let weekday = Calendar.current.component(.weekday, from: currentDate!)
let hour = Calendar.current.component(.hour, from: currentDate!)
let minutes = Calendar.current.component(.minute, from: currentDate!)
print("Weekday is \(weekday)")
print("Hour is \(hour)h")
print("Minutes are \(minutes)min")
ClosingPrices.append(closingPrice)
print("Closing prices count is \(ClosingPrices.count)")
print("Closing prices array is \(ClosingPrices)")
let oneDayLess = Calendar.current.date(byAdding: .day,value: -1, to: currentDate!)
let oneDayLessString = dateFormatter.string(from: oneDayLess!)
followingDate = oneDayLessString
print("Following date minus one day is \(followingDate)")
}
}
print(DatesArray)
print("Closing prices are \(ClosingPrices)")
return ClosingPrices
}
AddDates()
case .failure(let error):
print(error)
}
}
} // End of get1HPriceArr()
Is there any way to append all elements from the array (In the same order as the api gives) without having to pass in the date programatically by myself? Or is any other way I could achieve the same result?
回答1:
This is a standalone suggestion with Decodable
. It's much more versatile than SwiftyJSON. You can test it in a Playground.
The result
is an array of struct Price
sorted by date
descending. An array is preferable because a dictionary is unordered.
The JSON
let jsonString = """
{
"Meta Data": {
"1. Information": "Intraday (60min) open, high, low, close prices and volume",
"2. Symbol": "AMZN",
"3. Last Refreshed": "2018-08-15 12:30:00",
"4. Interval": "60min",
"5. Output Size": "Full size",
"6. Time Zone": "US/Eastern"
},
"Time Series (60min)": {
"2018-08-15 12:30:00": {
"1. open": "1892.1620",
"2. high": "1898.0000",
"3. low": "1879.3600",
"4. close": "1892.5000",
"5. volume": "893752"
},
"2018-08-15 11:30:00": {
"1. open": "1873.4399",
"2. high": "1893.3500",
"3. low": "1873.4399",
"4. close": "1892.1200",
"5. volume": "798959"
},
"2018-08-15 10:30:00": {
"1. open": "1905.1899",
"2. high": "1915.5300",
"3. low": "1871.0200",
"4. close": "1871.0200",
"5. volume": "1614045"
}
}
}
"""
The structs
struct Stocks: Decodable {
enum CodingKeys: String, CodingKey { case timeSeries = "Time Series (60min)"}
struct TimeSerie: Decodable {
let close : String
enum CodingKeys: String, CodingKey { case close = "4. close" }
}
let timeSeries : [String:TimeSerie]
}
struct Price {
let date : Date
let price : Double
}
The parsing
let data = Data(jsonString.utf8)
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
do {
let stocks = try decoder.decode(Stocks.self, from: data)
let result = stocks.timeSeries.map { (key: String, value: Stocks.TimeSerie) -> Price in
return Price(date: dateFormatter.date(from: key)!, price: Double(value.close)!)
}.sorted(by: {$0.date > $1.date})
print(result)
} catch { print(error) }
来源:https://stackoverflow.com/questions/51863329/how-to-append-all-elements-from-an-element-of-json-dictionary-to-an-array-with-a