Tom G Tom G - 20 days ago 7
iOS Question

How to populate data from NSMutableArray into struct using Swift3

I'v created a struct and I want to populate it with my data.

My struct:

struct CrimeNameSection {

var firstChar: Character
var name: [String]
var detail: [String]
var time: [String]


init(firstLetter: Character, object1: [String], object2: [String], object3: [String]) {

firstChar = firstLetter // First letter of 'name'
name = object1
detail = object2
time = object3
}


The first value of my struct ('firstChar') should hold the first letter in 'name' to create an alphabetic sections in tableView, the rest ('name','detail','time') should hold the data from my database (three columns: name, detail, time).

My code:

var marrCrimesData : NSMutableArray! // Hold the database

func getSectionsFromData() -> [CrimeNameSection] {
guard marrCrimesData != nil else {
return []
}


var sectionDictionary = [CrimeNameSection]()
for crime in marrCrimesData {
let crime = crime as! CrimesInfo
let firstChar = CrimeNameSection(firstLetter: crime.name[crime.name.startIndex], object1: [crime.name], object2: [crime.detail], object3: [crime.time])
if var names = firstChar {
names.append(crime.name)
sectionDictionary[firstChar] = names
} else {
sectionDictionary[firstChar] = [crime.name]
}
}

let sections = sectionDictionary.map { (key, value) in
return CrimeNameSection(firstLetter: key, name: value)
}
let sortedSections = sections.sorted { $0.firstLetter < $1.firstLetter }

return sortedSections
}


I get errors all over the place, I need help with storing the data inside my struct and sort it alphabetically.
Thank you all

Rob Rob
Answer

Consider

struct Crime {
    let name: String
    let detail: String
    let time: String
}

let crimes = [
    Crime(name: "Foo", detail: "detail 1", time: "9am"),
    Crime(name: "Bar", detail: "detail 2", time: "10am"),
    Crime(name: "Baz", detail: "detail 3", time: "11am"),
    Crime(name: "Qux", detail: "detail 4", time: "12am")
]

One approach is to just build an dictionary indexed by the first character and then sort it:

var crimeIndex = [Character: [Crime]]()
for crime in crimes {
    if let firstCharacter = crime.name.characters.first {
        if crimeIndex[firstCharacter] == nil {
            crimeIndex[firstCharacter] = [crime]
        } else {
            crimeIndex[firstCharacter]?.append(crime)
        }
    }
}

let sortedIndex = crimeIndex.sorted { $0.0 < $1.0 }

The advantage of the above is that we can use the dictionary to efficiently find the section. If you really want to use your custom "name section" structure, I'd first make it to use an array of Crime objects (having disjointed arrays of the properties of a Crime can be fragile, e.g. if you later decide to add sorting of the crimes). So it might look like:

struct CrimeNameSection {
    let firstCharacter: Character
    var crimes: [Crime]
}

And because we've lost some of the Dictionary efficiency for finding the index and have manually iterate through looking for the section, and I'll go ahead and do an insertion sort at the time, saving me from having to do a separate sort later:

var crimeSections = [CrimeNameSection]()
for crime in crimes {
    if let firstCharacter = crime.name.characters.first {
        var hasBeenAdded = false

        for (index, crimeIndex) in crimeSections.enumerated() {
            if firstCharacter == crimeIndex.firstCharacter {  // if we found section, add to it
                crimeSections[index].crimes.append(crime)
                hasBeenAdded = true
                break
            }
            if firstCharacter < crimeIndex.firstCharacter {   // if we've passed where the section should have been, insert new section
                crimeSections.insert(CrimeNameSection(firstCharacter: firstCharacter, crimes: [crime]), at: index)
                hasBeenAdded = true
                break
            }
        }

        // if we've gotten to the end and still haven't found section, add new section to end

        if !hasBeenAdded {
            crimeSections.append(CrimeNameSection(firstCharacter: firstCharacter, crimes: [crime]))
        }
    }
}
Comments