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. 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.
My model looks like this:
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 (i.e. removing duplicates) I now have an array of 50 to 100 structs. Currently multiple structs repeat the same Brand and then have ONE distinct 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. 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. Here's my current function which I'm embarrassed to show here – but in the interests of learning. 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. 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); 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.
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. Probably the better solution is to write one function to go from JSON to the form I need. 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
}