Swift - группировка данных по дням в словаре - PullRequest
0 голосов
/ 19 мая 2019

Это словарь:

[1549424985: ["amount": "10.0", "symbol": "XRP"], 1546531017: ["amount": "1.0", "symbol": "ETH"], 1549424153: ["amount": "50.0", "symbol": "EOS"], 1546531031: ["amount": "200.0", "symbol": "XRP"]]

typealias CryptoTuple = (symbol: String, amount: Double)
var cryptosPerDay = [String: [CryptoTuple]]()
var prev = ""
groupedData.keys.sorted(by: <).forEach( { key in
    let date = parseToDateString(key)
    if let buy = groupedData[key], let symbol = buy["symbol"], let amountStr = buy["amount"], let amount = Double(amountStr) {
        if prev != date {
            cryptosPerDay[date] = [(symbol, amount)]
            if let old = cryptosPerDay[prev] {
                cryptosPerDay[date]?.append(contentsOf: old)
            }
            prev = date
        } else {
            cryptosPerDay[date]?.append((symbol, amount))
        }
    }
})

Это код, полученный из ответа на мой предыдущий вопрос: Вычисление двойных чисел по датам в формате timeIntervalSince1970 из 2 разных словарей

но этот код возвращает мне повторные значения печать (cryptosPerDay) ["2019-02-06": [(symbol: "EOS", amount: 50.0), (symbol: "ETH", amount: 1.0), (symbol: "XRP", amount: 200.0), (symbol: "XRP", amount: 10.0)], "2019-01-03": [(symbol: "ETH", amount: 1.0), (symbol: "XRP", amount: 200.0)]]

Как это исправить?

Ответы [ 4 ]

1 голос
/ 19 мая 2019

Неясно, какова цель, но вот способ сделать то, что я думаю вы пытаетесь сделать. Обратите внимание на использование структур, а не кортежей:

let d = [ // your data
    1549424985: ["amount": "10.0", "symbol": "XRP"],
    1546531017: ["amount": "1.0", "symbol": "ETH"],
    1549424153: ["amount": "50.0", "symbol": "EOS"],
    1546531031: ["amount": "200.0", "symbol": "XRP"]
]
struct Trade {
    let amount:Double
    let symbol:String
}
var result = [String:[Trade]]()
for key in d.keys {
    let date = Date(timeIntervalSince1970: Double(key))
    let f = DateFormatter()
    f.dateFormat = "yyyy-MM-dd"
    let dayString = f.string(from: date)
    let val = d[key] as! [String:String]
    let trade = Trade(
        amount: Double(val["amount"]!)!,
        symbol: val["symbol"]!
    )
    result[dayString, default:[]].append(trade)
}

После этого result будет:

["2019-02-06": [Trade(amount: 10.0, symbol: "XRP"), Trade(amount: 50.0, symbol: "EOS")], 
 "2019-01-04": [Trade(amount: 1.0, symbol: "ETH"), Trade(amount: 200.0, symbol: "XRP")]]

... что кажется "правильным" ответом. Лично я думаю, что глупо использовать строки дат таким образом; было бы лучше использовать фактические даты. В этом случае последняя часть может быть переписана как:

var result = [Date:[Trade]]()
for key in d.keys {
    let date = Date(timeIntervalSince1970: Double(key))
    let day = Calendar(identifier: .gregorian).startOfDay(for: date)
    let val = d[key] as! [String:String]
    let trade = Trade(
        amount: Double(val["amount"]!)!,
        symbol: val["symbol"]!
    )
    result[day, default:[]].append(trade)
}
1 голос
/ 19 мая 2019

Ответ на другой вопрос далеко не идеален.Вот гораздо более простой способ конвертировать ваши [Int64: [String: String]] в группы по дням (используя Date, а не String).

let cryptoData: [Int64: [String: String]] = [
    1549424985: ["amount": "10.0", "symbol": "XRP"],
    1546531017: ["amount": "1.0", "symbol": "ETH"],
    1549424153: ["amount": "50.0", "symbol": "EOS"],
    1546531031: ["amount": "200.0", "symbol": "XRP"],
    ]

let cryptosPerDay = cryptoData.reduce(into: [Date: [[String: String]]]()) { (result, element) in
    result[Calendar.current.startOfDay(for: Date(timeIntervalSince1970: TimeInterval(element.key))), default: []].append(element.value)
}

print(cryptosPerDay)

Вывод:

[2019-01-03 07:00:00 +0000: [["amount": "1.0", "symbol": "ETH"], ["symbol": "XRP", "amount": "200.0"]], 2019-02-05 07:00:00 +0000: [["symbol": "EOS", "amount": "50.0"], ["amount": "10.0", "symbol": "XRP"]]]

Имейте в виду, что клавиши Date соответствуют полуночному местному времени.Ваши результаты будут немного отличаться в зависимости от вашего часового пояса.

Или с вашим CryptoTuple:

let cryptosPerDay = cryptoData.reduce(into: [Date: [CryptoTuple]]()) { (result, keyvalue) in
    result[Calendar.current.startOfDay(for: Date(timeIntervalSince1970: TimeInterval(keyvalue.key))), default: []].append((symbol: keyvalue.value["symbol"]!, amount: Double(keyvalue.value["amount"]!)!))
}
0 голосов
/ 19 мая 2019

Сначала вам нужно будет проверить, есть ли в массиве кортеж для данного символа и, если это так, объединить его и новый вместо добавления нового кортежа.

Измените предложение else в конце цикла на

if let index = cryptosPerDay[date]?.firstIndex(where: {$0.symbol == symbol}) {
    let total = (cryptosPerDay[date]?[index].amount ?? 0) + amount
    cryptosPerDay[date]?[index] = (symbol, total)
} else {
    cryptosPerDay[date]?.append((symbol, amount))
}
0 голосов
/ 19 мая 2019

Вы можете использовать Словарь (группировка: по:) , чтобы сгруппировать элементы любой последовательности, включая словарь.

let result:[String:[[String:String]]] = Dictionary(grouping: cryptoData) { parseToDateString($0.key) }.mapValues { $0.compactMap({ $0.value }) }
print(result)

["2019-01-03": [["amount": "200.0", "symbol": "XRP"], ["amount": "1.0", "символ": "ETH"]],

"2019-02-06": [["amount": "10.0", "symbol": "XRP"], ["amount": "50.0", "symbol": "EOS"]]]]


Использование кортежа

let result:[String:[CryptoTuple]] = Dictionary(grouping: cryptoData) { parseToDateString($0.key) }.mapValues { $0.compactMap({
    if let symbol = $0.value["symbol"], let amountStr = $0.value["amount"], let amount = Double(amountStr) {
        return (symbol, amount)
    } else { return nil }
}) }

Использование Struct

struct Crypto {
    var symbol: String
    var amount: Double
    init?(_ dict:[String: String]) {
        if let symbol = dict["symbol"], let amountStr = dict["amount"], let amount = Double(amountStr) {
            self.symbol = symbol
            self.amount = amount
        } else {
            return nil
        }
    }
}
let result:[String:[Crypto]] = Dictionary(grouping: cryptoData) { parseToDateString($0.key) }.mapValues { $0.compactMap({ Crypto($0.value) }) }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...