【问题标题】:Convert dictionary values from string to double when there are 5 values per key当每个键有 5 个值时,将字典值从字符串转换为双精度
【发布时间】:2021-09-26 00:36:53
【问题描述】:

我正在尝试学习如何在 Swift 中使用 Dictionary。我的程序读取一个本地 JSON 文件,该文件包含每个键的五个值元素,并将其放入字典中。键是字符串格式“yyyy-MM-dd”的日期,值表示字符串格式的美元 409.34593。我已经能够对字典进行排序和过滤。

此外,使用 mapValues() 创建一个新字典,其中包含一个键和一个值,并将值转换为可选的双精度值。我不明白的是如何将所有 5 个值元素转换为双精度值,即拥有一个与从 JSON 文件中读取的相同但所有 5 个值都为双精度值或可选双精度值的字典。

下面是 JSON 文件和我的代码的 sn-p。如果您能指出我正确的方向,我们将不胜感激。

"Time Series (Daily)": {
    "2021-09-22": {
        "1. open": "402.1700",
        "2. high": "405.8500",
        "3. low": "401.2600",
        "4. close": "403.9000",
        "5. volume": "5979811"
    },
    "2021-09-21": {
        "1. open": "402.6600",
        "2. high": "403.9000",
        "3. low": "399.4400",
        "4. close": "400.0400",
        "5. volume": "6418124"
    },

struct Stock: Codable {
    let timeSeriesDaily: [String : TimeSeriesDaily]  // creates a dictionary, key = string : TimeSeriesDaily = value
    enum CodingKeys: String, CodingKey {
        case timeSeriesDaily = "Time Series (Daily)"
    }
}
struct TimeSeriesDaily: Codable {
    let Open, High, Low, Close: String
    let Volume: String
    enum CodingKeys: String, CodingKey {
        case Open = "1. open"
        case High = "2. high"
        case Low = "3. low"
        case Close = "4. close"
        case Volume = "5. volume"
    }
}
class ReadData: ObservableObject  {
    @Published var tmpData = Stock(timeSeriesDaily : [:])  // initialize dictionary as empty when struct Stock is created
    // type info has to be made availabe upon creation, is done in the Stock struct
    init() {
        loadData()
    }
    func loadData() {
        guard let url = Bundle.main.url(forResource: "VOO", withExtension: "json")
        else {
            print("Json file not found")
            return
        }
        let decoder = JSONDecoder()
        do {
            let data = try Data(contentsOf: url)
            self.tmpData = try decoder.decode(Stock.self, from: data)
//            print(self.tmpData)
        } catch {
            print(error)
        }
    }
}
struct ContentView: View {
    @ObservedObject var vooData = ReadData()
    var body: some View {
        ScrollView  {
            VStack (alignment: .leading) {
                let lastYear = getOneYearAgo()
                let filteredDict = vooData.tmpData.timeSeriesDaily.filter { $0.key > lastYear } // Works
                let sortedFilteredDict = filteredDict.sorted { $0.key < $1.key }                 // Works
                let justCloseArray = sortedFilteredDict.map { ($0.key, $0.value.Close) }           // returns array [(String, String)]
                let justCloseDict = Dictionary(uniqueKeysWithValues: justCloseArray)                // returns dictionary with 1 key & 1 val
                let sortedCloseDict = justCloseDict.sorted { $0.key < $1.key }                      // works
                let newDict = filteredDict.mapValues { Double($0.Close) }
                let sortedNewDict = newDict.sorted { $0.key < $1.key }
                Spacer()
                //                ForEach ( sortedCloseDict.map { ($0.key, $0.value) }, id: \.0 ) { keyValuePair in
                ForEach ( sortedNewDict.map { ($0.key, $0.value) }, id: \.0 ) { keyValuePair in   // map converts dictionary to arrap
                    HStack {
                        Text (keyValuePair.0)
                        Text ("\(keyValuePair.1!)")
                    }
                }
                Spacer()
            } // end vstack
        } .frame(width: 600, height: 400, alignment: .center) // end scroll view
    }
}

【问题讨论】:

    标签: swift dictionary key-value


    【解决方案1】:

    这里的问题是你的美元价值不是数字,它们是字符串。

    把它放在操场上:

    import Foundation
    
    let jsonData = """
    {
      "Time Series (Daily)": {
        "2021-09-22": {
          "1. open": "402.1700",
          "2. high": "405.8500",
          "3. low": "401.2600",
          "4. close": "403.9000",
          "5. volume": "5979811"
        },
        "2021-09-21": {
          "1. open": "402.6600",
          "2. high": "403.9000",
          "3. low": "399.4400",
          "4. close": "400.0400",
          "5. volume": "6418124"
        }
      }
    }
    """
        .data(using: .utf8)!
    
    struct ReadData {
        let timeSeriesDaily: [Date: TimeSeriesDaily]
    }
    
    struct TimeSeriesDaily {
        let open: Double
        let high: Double
        let low: Double
        let close: Double
        let volume: Double
    }
    
    extension ReadData: Decodable {
        enum CodingKeys: String, CodingKey {
            case timeSeriesDaily = "Time Series (Daily)"
        }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            timeSeriesDaily = Dictionary(
                uniqueKeysWithValues: try container
                    .decode([String: TimeSeriesDaily].self, forKey: .timeSeriesDaily)
                    .map { (dateFormatter.date(from: $0)!, $1) }
            )
        }
    }
    /** In the above, the container can decode [String: T] but it can't decode [Date: T], so we convert the Strings to Dates using a dateFormatter */
    
    extension TimeSeriesDaily: Decodable {
        enum CodingKeys: String, CodingKey {
            case open = "1. open"
            case high = "2. high"
            case low = "3. low"
            case close = "4. close"
            case volume = "5. volume"
        }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            open = Double(try container.decode(String.self, forKey: .open))!
            high = Double(try container.decode(String.self, forKey: .high))!
            low = Double(try container.decode(String.self, forKey: .low))!
            close = Double(try container.decode(String.self, forKey: .close))!
            volume = Double(try container.decode(String.self, forKey: .volume))!
        }
    }
    /** Since the values in the json are actually Strings, we decode them as such and then convert to Doubles. */
    let dateFormatter: DateFormatter = {
        let result = DateFormatter()
        result.dateFormat = "yyyy-MM-dd"
        return result
    }()
    
    print(try JSONDecoder().decode(ReadData.self, from: jsonData))
    

    如果你不喜欢所有的强制转换,你可以引入一个castOrThrow 类型的函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 2012-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多