Home > Mobile >  Swift map nested dictionary to swap outer and inner keys
Swift map nested dictionary to swap outer and inner keys

Time:11-15

I'm building an app in swift which displays several graphs, one of which is a graph of exchange rates which can be filtered by currency code and date range within a UITableView. I'm able to successfully pull the FX data from https://fixer.io/ and convert that json data into the following swift struct:

struct FetchedFXRateByDate: Codable {
let success, timeseries: Bool
let startDate, endDate, base: String
let rates: [String: [String: Double]] // [Date: [Currency Code : Amount]]

enum CodingKeys: String, CodingKey {
    case success, timeseries
    case startDate = "start_date"
    case endDate = "end_date"
    case base, rates
}

}

What I now want, is to manipulate or 'map'/'filter' the inner dict 'let rates: [String: [String: Double]]', to convert the dict:

From:

[String: [String: Double]] // [Date: [Currency Code : Amount]]

To:

[String: [String: Double]] // [Currency Code: [Date : Amount]]

Effectively swapping the keys. This can easily be done by a for loop with the keys, but I need a more efficient way to achieve the task. This is so i can draw the graph in the below interface:

Graph Table View

Any help is greatly appreciated!

CodePudding user response:

A possible way is to use reduce(into:_:):

With sample:

let rates: [String: [String: Double]] = ["2021-11-14": ["CAD": 1.1,
                                                        "USD": 1.0,
                                                        "EUR": 0.9],
                                         "2021-11-15": ["CAD": 1.11,
                                                        "USD": 1.01,
                                                        "EUR": 0.91]]

This should do the trick:

let target = rates.reduce(into: [String: [String: Double]]()) { partialResult, current in
    let date = current.key
    let dayRates = current.value
    dayRates.forEach { aDayRate in
        var currencyRates = partialResult[aDayRate.key, default: [:]]
        currencyRates[date] = aDayRate.value
        partialResult[aDayRate.key] = currencyRates
    }
}

For the logic, we iterate over each elements of rates. For each [CurrencyCode: Amount], we iterate on them, and set them to partialResult (which at the end of the inner loop of reduce(into:_:) will be the finalResult, ie the returned value).

Outputs for print(rates) & print(target) (I just formatted them to make them easier to read):

$> ["2021-11-14": ["CAD": 1.1, "EUR": 0.9, "USD": 1.0], 
    "2021-11-15": ["USD": 1.01, "EUR": 0.91, "CAD": 1.11]]
$> ["EUR": ["2021-11-14": 0.9, "2021-11-15": 0.91], 
    "USD": ["2021-11-15": 1.01, "2021-11-14": 1.0], 
    "CAD": ["2021-11-14": 1.1, "2021-11-15": 1.11]]
  • Related