Home > Net >  Swift - Creating a new nested array based on the date values of an array consisting of a model
Swift - Creating a new nested array based on the date values of an array consisting of a model

Time:11-02

There is an array of users coming from the database.

struct Model {
    var name: String
    var date: String
}

let data: [Model] = [
    Model(name: "Mick", date: "12.09.2022"),
    Model(name: "Donald", date: "12.09.2022"),
    Model(name: "Melissa", date: "07.09.2022"),
    Model(name: "Kerem", date: "12.09.2022"),
    Model(name: "Caren", date: "24.09.2022"),
    Model(name: "Jhon", date: "07.09.2022"),
    Model(name: "Miles", date: "19.09.2022"),
    Model(name: "Rick", date: "19.09.2022"),
]

What I want is to create a new array by grouping the ones with the same date.

The array I want:

let groupedData: [[Model]] = [
    [
        Model(name: "Caren", date: "24.09.2022")
    ],
    [
        Model(name: "Rick", date: "19.09.2022"),
        Model(name: "Miles", date: "19.09.2022")
    ],
    [
        Model(name: "Mick", date: "12.09.2022"),
        Model(name: "Donald", date: "12.09.2022"),
        Model(name: "Kerem", date: "12.09.2022")
    ],
    [
        Model(name: "Jhon", date: "07.09.2022"),
        Model(name: "Melissa", date: "07.09.2022")
    ]

]

I tried to enter the master array with the for loop, but I could not come to any conclusion because the size of the future data is variable.

CodePudding user response:

One solution is to create a temporary dictionary keyed on date. The value for each date string is an array of matching models. The following produces the desired results though the final grouped data isn't sorted by date. Since date strings in the format dd-MM-yyyy can't be sorted as strings, I leave the date sorting as an exercise to the reader.

var temp = [String : [Model]]()
data.forEach {
    var dateArray = temp[$0.date] ?? []
    dateArray.append($0)
    temp[$0.date] = dateArray
}

let groupedData: [[Model]] = temp.map { $0.value }

The nice thing about this solution is that it only scans the list of records once and adds each to an array keyed by the record's date.


The whole temporary dictionary can actually be created using the Dictionary(grouping:,by:) initializer. The above code can be reduced into:

let groupedData: [[Model]] = Dictionary(grouping: data, by: { $0.date }).map { $0.value }

This gives the same result in much simpler code. The Dictionary(grouping: data, by: { $0.date }) part replaces the temp declaration and the data.forEach loop. The use of map { $0.value } at the end is the same as above giving the final array of arrays where each inner array has the same date.


Another solution that can be done in one line, but is vastly less efficient, is as follows:

let groupedData: [[Model]] = Set<String>(data.map { $0.date }).map { date in data.filter { $0.date == date } }

The problem with this solution is that the data array will get filtered for each unique date. If you have thousands of records with hundreds or thousands of different dates then the performance will be terrible.

To explain that line:

The Set<String>(data.map { $0.date }) part creates a set from all of the dates. This builds a unique list of dates from all of the records.

The rest .map { date in data.filter { $0.date == date } } creates an array by filtering the list of records with a matching date. This is done for each unique date.

  • Related