Home > Enterprise >  Nested Sort in Struct Array in Swift 5
Nested Sort in Struct Array in Swift 5

Time:12-24

Here its my model looks like

struct ContactModel{
    var alphabet: String
    var users: [UserModel]
}

struct UserModel{
    var firstname: String
    var lastname: String
}

Using sort alphabetically sorting working fine here Is the code:

contactArray.sort{ $0.alphabet < $1.alphabet }

Now, I want to sort ascending or descending order on bases of UserModel firstname I try with nested sorting but giving me different errors like

Comparable and Binary operator '<' cannot be applied to two '[UserModel]' operands

I search on StackOverflow regarding this but haven't find nested sorting and operands error solution.

I tried this way

let sortedStudents = contactArray.sorted { (lhs: ContactModel, rhs: ContactModel) -> Bool in
                let users1 = lhs.users.sorted(by: {
                    $0.firstname.lowercased() < $1.firstname.lowercased()
                })
                let users2 = rhs.users.sorted(by: {
                    $0.firstname.lowercased() < $1.firstname.lowercased()
                })
                
                return users1 < users2
            }

But its not working. The only problem here that I am not able to do nested sort here want to sort firstname of UserModel and the We have ContactModel Array

var contactArray = [ContactModel](){
        didSet{
            
            contactArray.sort{ $0.alphabet < $1.alphabet } //Simple Sort working fine.
            
            let sortedStudents = contactArray.sorted { (lhs: ContactModel, rhs: ContactModel) -> Bool in
                let users1 = lhs.users.sorted(by: {
                    $0.firstname.lowercased() < $1.firstname.lowercased()
                })
                let users2 = rhs.users.sorted(by: {
                    $0.firstname.lowercased() < $1.firstname.lowercased()
                })
                
                return users1 < users2
            }
            
            self.tableView.reloadData()
        }
    }

Array Contains this

 func getAllContacts() -> [ContactModel]{
        return [
            ContactModel(alphabet: "B", users: [
                UserModel(firstname: "Bhaswar", lastname: "Patel"),
                UserModel(firstname: "Bunty", lastname: "Patel")
            ]),
            ContactModel(alphabet: "N", users: [
                UserModel(firstname: "Nilesh", lastname: "Darji")
            ]),
            ContactModel(alphabet: "Y", users: [
                UserModel(firstname: "Yogesh", lastname: "Patel"),
                UserModel(firstname: "Yash", lastname: "Patel")
            ])
        ]
    }

Results:

Alphabetic section sorting working fine need to sort users now using firstname for this Yash is comes first then Yogesh

Any suggestion appreciated. Thanks!

CodePudding user response:

The code below is sorting the ContactModel array based on the alphabet first. Then, it maps this sorted array to a new array where the UserModel array is sorted as well.

contactArray.sort { $0.alphabet < $1.alphabet }
let fullySorted = contactArray.map( { ContactModel(alphabet: $0.alphabet, users: $0.users.sorted { $0.firstname < $1.firstname }) } )

The map and sort functions could also be swapped, i.e. first mapping to a new array and then sorting based on the alphabet. The original array:

var contactArray = [
    ContactModel(alphabet: "B", users: [
        UserModel(firstname: "Bhaswar", lastname: "Patel"),
        UserModel(firstname: "Bunty", lastname: "Patel")
    ]),
    ContactModel(alphabet: "N", users: [
        UserModel(firstname: "Nilesh", lastname: "Darji")
    ]),
    ContactModel(alphabet: "Y", users: [
        UserModel(firstname: "Yogesh", lastname: "Patel"),
        UserModel(firstname: "Yash", lastname: "Patel")
    ])
]

will become (Yash and Yogesh are now alphabetically ordered):

var contactArray = [
    ContactModel(alphabet: "B", users: [
        UserModel(firstname: "Bhaswar", lastname: "Patel"),
        UserModel(firstname: "Bunty", lastname: "Patel")
    ]),
    ContactModel(alphabet: "N", users: [
        UserModel(firstname: "Nilesh", lastname: "Darji")
    ]),
    ContactModel(alphabet: "Y", users: [
        UserModel(firstname: "Yash", lastname: "Patel"),
        UserModel(firstname: "Yogesh", lastname: "Patel")
    ])
]

If there would be another ContactModel with alphabet = "A", then this would've been the first one in the contactArray.

You could even reduce it to a single line of code if you really want. However, I wouldn't recommend it because sorting, and mapping can become hard to read quite easily for people that aren't familiar with it.

// Single line of code solution (I wouldn't recommend this because of readability)
let fullySorted = contactArray.sorted { $0.alphabet < $1.alphabet }.map( { ContactModel(alphabet: $0.alphabet, users: $0.users.sorted { $0.firstname < $1.firstname }) } )
  • Related