Отделение строки от файла CSV

В настоящее время я разрешаю пользователям импортировать CSV-file создан из Excel или же Numbers, Лист имеет несколько полей, и я могу импортировать их без проблем.

Однако одно из полей выглядит так:

"[Title1: (100: S11), (100: S12), (100: 10), (100: 2), (100: 3)], [Title2: (300: 50), (300: 90), (300: 100a), (300: D), (300: E)], [Title3: (500: 2), (500: 112), (500: 6), (500: 110), (500: 113)]"

Как я должен разделить это поле в словарь, похожий на этот:

//[String: [[Int: String]]]

[
    "Title1": [[100: "S11"], [100: "S12"], [100:   "10"], [100:   "2"], [100:   "3"]],
    "Title2": [[300:  "50"], [300:  "90"], [300: "100a"], [300:   "D"], [300:   "E"]],
    "Title3": [[500:   "2"], [500: "112"], [500:    "6"], [500: "110"], [500: "113"]]
]

Это поле создается самим пользователем, и это уже кажется слишком сложным и подверженным ошибкам. Возможно, лучшим вопросом было бы, как лучше отформатировать это, а не разбивать это поле на словарь, который я хочу?


Возможно упрощенная версия:

//[String: [Int: [String]]]

[
    "Title1": [100: ["S11", "S12",   "10",   "2",   "3"]],
    "Title2": [300:  ["50",  "90", "100a",   "D",   "E"]],
    "Title3": [500:   ["2", "112",    "6", "110", "113"]]
]

Некоторое объяснение об этой области:

Название: может быть любым, это название вложенного словаря

Ключ во вложенном словаре может быть 100, 200, 300, 400, 500 or 600

Значение во вложенном словаре является String который формирует уникальный ключ вместе с ключом в этом вложенном словаре.

Это означает:

100: "S11" and 200: "S11" // valid
100: "S11" and 100: "S11" // not valid

extension String {

    // Separate alphanumeric characters
    func split(_ number: Int = 1) -> [String] {
        let number = max(number, 1)

        return self.components(separatedBy: CharacterSet.alphanumerics.inverted).filter { $0.characters.count > number - 1 }
    }
}

let string = "Appetizer: (100: S11), (100: S12), (100: 10), (100: 2), (100: 3) * Main: (300: 50), (300: 90), (300: 100a), (300: D), (300: E) * Dessert: (500: 2), (500: 112), (500: 6), (500: 110), (500: 113)"

let values = string.components(separatedBy: "*").map { $0.split() }

var result: [String: [Int: [String]]] = [:]

values.forEach {
    guard let key = $0.first else { return }

    result[key] = result[key] ?? [:]

    let values = $0.dropFirst()

    let name = values.enumerated().filter { $0.0 % 2 == 0 }.map { $0.1 }
    let code = values.enumerated().filter { $0.0 % 2 != 0 }.map { $0.1 }

    zip(name, code).forEach {
        guard let category = Int($0.0) else { return }

        result[key]?[category] = (result[key]?[category] ?? []) + [$0.1]
    }
}

Этот код дает желаемый результат, но он кажется очень уродливым и слишком сильно зависит от расположения значений (вместо проверки значений).

Любое предложение о том, как сделать это, используя гораздо более простой и элегантный способ?

2 ответа

Я предполагаю, что вы остановились на первом формате экспорта, т.е.

[Title1: (100: S11), (100: S12), (100: 10), (100: 2), (100: 3)], [Title2: (300: 50), (300: 90), (300: 100a), (300: D), (300: E)], [Title3: (500: 2), (500: 112), (500: 6), (500: 110), (500: 113)]

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

extension String {
    subscript(_ range: NSRange) -> String {
        let startIndex = self.utf16.index(self.utf16.startIndex, offsetBy: range.location)
        let endIndex = self.utf16.index(startIndex, offsetBy: range.length)

        return String(describing: self.utf16[startIndex..<endIndex])
    }
}

let str = "[Title1: (100: S11), (100: S12), (100: 10), (100: 2), (100: 3)], [Title2: (300: 50), (300: 90), (300: 100a), (300: D), (300: E)], [Title3: (500: 2), (500: 112), (500: 6), (500: 110), (500: 113)]"
let regex1 = try! NSRegularExpression(pattern: "\\[(.+?): (.+?)\\]", options: [])
let regex2 = try! NSRegularExpression(pattern: "\\((\\d+?): (.+?)\\)", options: [])

var result = [String: [Int: [String]]]()

regex1.enumerateMatches(in: str, options: [], range: NSMakeRange(0, str.utf16.count)) { match1, _, _ in
    guard let match1 = match1 else { return }

    let title = str[match1.rangeAt(1)]
    let categoriesStr = str[match1.rangeAt(2)]
    var categories = [Int: [String]]()

    regex2.enumerateMatches(in: categoriesStr, options: [], range: NSMakeRange(0, categoriesStr.utf16.count)) { match2, _, _ in
        guard let match2 = match2 else { return }

        let number = Int(categoriesStr[match2.rangeAt(1)])!
        let identifier = categoriesStr[match2.rangeAt(2)]

        if categories[number] == nil {
            categories[number] = [String]()
        }
        categories[number]!.append(identifier)
    }

    result[title] = categories
}

print(result)

Следующий код должен анализировать предоставленные вами данные в словарь так, как вам нужно:

    let data = "[Title1: (100: S11), (100: S12), (100: 10), (100: 2), (100: 3)], [Title2: (300: 50), (300: 90), (300: 100a), (300: D), (300: E)], [Title3: (500: 2), (500: 112), (500: 6), (500: 110), (500: 113)]"

    let arr1 = data.components(separatedBy:"],")
    var dic = [String:[[String:Any]]]()
    for row1 in arr1 {
        let arr2 = row1.components(separatedBy:",")
        var key = ""
        for row2 in arr2 {
            var txt = row2.replacingOccurrences(of:" ", with:"")
            if txt.hasPrefix("[Title") {
                // First row
                txt = txt.replacingOccurrences(of:"[", with:"")
                let arr3 = txt.components(separatedBy:":")
                key = arr3[0]
                let key1 = arr3[1].replacingOccurrences(of:"(", with:"")
                let val1 = arr3[2].replacingOccurrences(of:")", with:"").replacingOccurrences(of:" ", with:"")
                dic[key] = [[key1:val1]]
            } else {
                let arr3 = row2.components(separatedBy:":")
                let key1 = arr3[0].replacingOccurrences(of:"(", with:"").replacingOccurrences(of:" ", with:"")
                let val1 = arr3[1].replacingOccurrences(of:")", with:"").replacingOccurrences(of:"]", with:"").replacingOccurrences(of:" ", with:"")
                dic[key]?.append([key1:val1])
            }
        }
    }
    print(dic)

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

Другие вопросы по тегам