简体   繁体   English

需要帮助在 Swift 中重组结构数组中的数据

[英]Need help reorganizing data in array of structs in Swift

I feel like the answer here should be obvious and I should be able to sort it out myself, but after a full day of struggling I think it makes sense to ask for help.我觉得这里的答案应该很明显,我应该能够自己解决,但经过一整天的挣扎,我认为寻求帮助是有道理的。 I have an array of structs that I have created by first decoding a JSON data file and then filtering part of its elements into my own model.我有一个结构数组,我首先解码 JSON 数据文件,然后将其部分元素过滤到我自己的 model 中。 I need to reorganize the data so that I can use it to populate 3 linked pickers, where a user will choose specific search parameters.我需要重新组织数据,以便我可以使用它来填充 3 个链接的选择器,用户将在其中选择特定的搜索参数。

My model looks like this:我的 model 看起来像这样:

    struct Brand: Hashable, Comparable {
    static func == (lhs: Brand, rhs: Brand) -> Bool {
        return lhs.name == rhs.name && lhs.models == rhs.models
    }
    
    static func <(lhs: Brand, rhs: Brand) -> Bool {
        return lhs.name < rhs.name
    }
    var name: String
    var models: [Model]

    init(name:String, models:[Model]) {
        self.name = name
        self.models = models
    }
}

struct Model: Hashable, Comparable {
    static func == (lhs: Model, rhs: Model) -> Bool {
        return lhs.name == rhs.name && lhs.years == rhs.years
    }
    
    static func <(lhs: Model, rhs: Model) -> Bool {
        return lhs.name < rhs.name
    }
    var name: String
    var years: [String]

    init(name:String, years:[String]) {
        self.name = name
        self.years = years
    }
}

After passing in data from the JSON and doing some reorganizing (ie removing duplicates) I now have an array of 50 to 100 structs.从 JSON 传入数据并进行一些重组(即删除重复项)后,我现在有一个包含 50 到 100 个结构的数组。 Currently multiple structs repeat the same Brand and then have ONE distinct Model.目前多个结构重复相同的品牌,然后有一个不同的 Model。 I need to reorganize it so that each struct has only a SINGLE brand, but within it there is an array of models.我需要重新组织它,以便每个结构只有一个单一品牌,但其中有一系列模型。 The data structure is obviously set up for this.数据结构显然是为此设置的。 Here's what the data currently looks like (from a console dump):以下是数据当前的样子(来自控制台转储):

▿ 5 elements
▿ PickerTesting.Brand
    - name: "Cannondale"
    ▿ models: 1 element
    ▿ PickerTesting.Model
        - name: "SystemSix Carb"
        ▿ years: 1 element
        - "2020"
▿ PickerTesting.Brand
    - name: "Cannondale"
    ▿ models: 1 element
    ▿ PickerTesting.Model
        - name: "SuperX Wmn\'s"
        ▿ years: 1 element
        - "2020"
▿ PickerTesting.Brand
    - name: "Cannondale"
    ▿ models: 1 element
    ▿ PickerTesting.Model
        - name: "Synapse Carb"
        ▿ years: 1 element
        - “2020"
PickerTesting.Brand
    - name: "Pinarello"
    ▿ models: 1 element
    ▿ PickerTesting.Model
        - name: "Razha"
        ▿ years: 1 element
        - "2021"
▿ PickerTesting.Brand
    - name: "Pinarello"
    ▿ models: 1 element
    ▿ PickerTesting.Model
        - name: "Angliru"
        ▿ years: 1 element
        - "2021"

AND I NEED TO GET IT TO LOOK LIKE THIS:我需要让它看起来像这样:

▿ 2 elements
▿ PickerTesting.Brand
    - name: "Cannondale"
    ▿ models: 3 element
    ▿ PickerTesting.Model
        - name: "SystemSix Carb”, “SuperX Wmn”, “Synapse Carb"
        ▿ years: 1 element
        - “2020"
PickerTesting.Brand
    - name: "Pinarello"
    ▿ models: 2 element
    ▿ PickerTesting.Model
        - name: “Razha”, “Angliru"
        ▿ years: 1 element
        - "2021"

I've been working to try to loop through the items with the same brand name, copy the Models(and thus years) into an empty array, and then at the end of repeated Brand names, append the Models to the single brand.我一直在尝试遍历具有相同品牌名称的项目,将模型(以及年份)复制到一个空数组中,然后在重复品牌名称的末尾,append 模型到单个品牌。 That logic still seems right to me and I've been able to combine some models under one brand.这种逻辑对我来说似乎仍然是正确的,我已经能够将一些模型组合在一个品牌下。 But my code is an absolute mess: what I have now keeps repeating over the same Brands and produce repeat structs with multiple models.但是我的代码绝对是一团糟:我现在不断重复相同的品牌并产生具有多个模型的重复结构。 Worse still, my previously successful use of Array(Set(Data)) to remove duplicates fails.更糟糕的是,我之前成功使用 Array(Set(Data)) 删除重复项失败了。 Here's my current function which I'm embarrassed to show here – but in the interests of learning.这是我目前的 function 在这里我很尴尬地展示 - 但为了学习。 The counter is obviously wrong, but my attempt to use for index in range loops led to either infinite loops or 10s of thousands of items output.计数器显然是错误的,但我尝试在范围循环中使用索引导致无限循环或成千上万个项目 output。 I'm still not sure why a counter value of only 10 will produce SO MANY results, but the results never move past the first two Brands (there are about 10);我仍然不确定为什么只有 10 的计数器值会产生如此多的结果,但结果永远不会超过前两个品牌(大约有 10 个); instead, they just keep repeating results there.相反,他们只是在那里不断重复结果。 Note: in the code below the uglyData taken as an argument to this function is my current array of structs, as described above.注意:在下面的代码中,作为 function 的参数的丑陋数据是我当前的结构数组,如上所述。

I apologize that this isn't more succinct, and I would be enormously grateful for any guidance .我很抱歉这不是更简洁,我将非常感谢任何指导 To repeat, I feel like this just shouldn't be too hard, but I'm at my whit's end.再说一遍,我觉得这不应该太难,但我已经走到了尽头。

func combineSort (uglyData: [Brand]) -> [Brand] {
    var cleanData: [Brand] = []
    var newModels = [Model]()
    var counter = 0
        while counter < 10 {
            if uglyData[counter].name == uglyData[counter+1].name {
                newModels.append(contentsOf: uglyData[counter].models)
                }
            else if uglyData[counter].name != uglyData[counter+1].name {
                cleanData.append(Brand(name: uglyData[counter].name, models: newModels))
                newModels = []
                }
            counter += 1
        }
    return cleanData
}

As Leo Dabus requested, here's the function that parses the JSON data the first time through.根据 Leo Dabus 的要求,这是第一次解析 JSON 数据的 function。 Probably the better solution is to write one function to go from JSON to the form I need.可能更好的解决方案是将 function 从 JSON 写入 go 到我需要的形式。 I'm working on that now, but not necessarily close to success.我现在正在努力,但不一定接近成功。

func getBrandsAll() -> [Brand] {
var brands: [Brand] = []
let allFrames = frameData
    for i in 0..<allFrames.count {
        brands.append(Brand(name: allFrames[i].brand, models: 
        [Model(name:  allFrames[i].model, years: [allFrames[i].year.description])]))
    }
    let unique = Array(Set(brands))
    let sorted = unique.sorted()
    return sorted
}

I would, do that directly in the parsing of the JSON function.我会直接在 JSON function 的解析中执行此操作。 By maybe using a dictionary with the brand name as key and an array of models as value.通过可能使用以品牌名称作为键和模型数组作为值的字典。 hence when you parse your json, you can check if the key exists if it doesn't exist you add it to the dictionary with an empty array, or an array with the first model.因此,当您解析 json 时,您可以检查该键是否存在,如果它不存在,您可以将其添加到带有空数组的字典中,或者带有第一个 model 的数组中。

In the end you can convert each element of your dictionary into a Brand最后,您可以将字典中的每个元素转换为品牌

var brands: [String:[Model]] = []
let allFrames = frameData
var currentBrand:String
for i in 0..<allFrames.count {
    currentBrand = allFrames[i].brand
    if(brands[currentBrand] == nil)
    {
        brands[currentBrand] = [Model(name:  allFrames[i].model, years: [allFrames[i].year.description])]
    }
    else
    {
    brands[currentBrand]?.append(Brand(name: allFrames[i].brand, models: 
    [Model(name:  allFrames[i].model, years: [allFrames[i].year.description])]))
    }
}    

Then you can go over this dictionary and create you Brand object然后你可以在这本字典上 go 并创建你的品牌 object

Answering my own question, after a lot of help from a good friend with way more knowledge than I, 25 years of coding experience, and a lot of patience.在一位知识比我多的好朋友的大力帮助下,回答了我自己的问题,25 年的编码经验,以及很大的耐心。 Thanks also to La pieuvre for help.也感谢 La pieuvre 的帮助。 The result takes a similar start to his suggestion, but it uses a second dictionary to store Brand/Model and an index location.结果与他的建议类似,但它使用第二个字典来存储品牌/型号和索引位置。 I also changed the data structure to make the years a Set rather than array.我还更改了数据结构以使年份成为集合而不是数组。 This way neither Brand nor Model are duplicated, and while years are, the duplicates don't get added to the set.这样一来,Brand 和 Model 都不会被复制,虽然年份是重复的,但不会将复制品添加到集合中。

Here's the code:这是代码:

    func getBrandsAll() -> [Brand] {
    var brands: [String : Brand] = [:]
    var modelHelper: [String : [String : Int]] = [:]
    let allFrames = frameData
    
    for i in 0..<allFrames.count {
        let brand = allFrames[i].brand
        if brands[brand] == nil {
            brands[brand] = Brand(name: brand,
                models: [Model(name: allFrames[i].model, years: [allFrames[i].year])])

            modelHelper[brand] = [allFrames[i].model : 0]
        }
        else if modelHelper[brand]?[allFrames[i].model] == nil {
            brands[brand]?.models.append(Model(name: allFrames[i].model,
                years: [allFrames[i].year]))

            modelHelper[brand]?[allFrames[i].model] = (brands[brand]?.models.count)! - 1
        }
        else {
            let modelIdx = modelHelper[brand]?[allFrames[i].model] ?? 0
            brands[brand]?.models[modelIdx].years.insert(allFrames[i].year)
        }
    }
    var brandslist: [Brand] = []
        for (_,value) in brands {
            brandslist.append(value)
            }
    return brandslist.sorted()
}

The results are data that look like this:结果是如下所示的数据:

▿ PickerTesting.Brand
- name: "Lynskey"
▿ models: 5 elements
    ▿ PickerTesting.Model
    - name: "Roadback Touring"
    ▿ years: 1 member
        - 2020
    ▿ PickerTesting.Model
    - name: "R300 Road"
    ▿ years: 2 members
        - 2019
        - 2020
    ▿ PickerTesting.Model
    - name: "R500 Disc"
    ▿ years: 1 member
        - 2020
    ▿ PickerTesting.Model
    - name: "GR Race Gravel"
    ▿ years: 1 member
        - 2020
    ▿ PickerTesting.Model
    - name: "GR300 Gravel"
    ▿ years: 1 member
        - 2020

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM