# Sort an Array of Custom Objects in Swift

Hi all readers, Happy Valentine’s Day! 💕

Today we’re going to explore how we can sort an array of custom objects by the value of their properties.

The source code of this post is available [here](https://github.com/xavier7t/DemoSortArraysOfCustomObj20230214).

# Step 0 - Overview

Say I’m working with an array of objects of `Reminder`, which has the following properties:

1. ID - UUID
    
2. desc - String
    
3. isFavorite - Bool
    
4. dueDate - Date
    

And this array with a random order to be sorted, so that favorite reminders and be displayed at the top, and then ascending by its due date.

Now let’s Xcode and figure out how!

# Step 1 - Prepare the data source

Create a new project and add a `.swift` file (select Swift file under source, instead of SwiftUI view).

In this file, declare a struct named `Reminder` with the properties mentioned above.

Then add two extensions - for the `Date` and `String` class, so we can convert a date to a string and vice versa.

Lastly, declare an array of `Reminder` objects. With different values of `isFavorite` and `dueDate`.

```swift
struct Reminder {
    let id: UUID
    let desc: String
    let isFavorite: Bool
    let dueDate: Date
    
    init(desc: String, isFavorite: Bool, dueDate: Date) {
        self.id = UUID()
        self.desc = desc
        self.isFavorite = isFavorite
        self.dueDate = dueDate
    }
}

extension String {
    func asDateYYYYMMDD() -> Date {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMdd"
        return dateFormatter.date(from: self) ?? Date()
    }
}

extension Date {
    func asStringYYYYMMDD() -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMdd"
        return dateFormatter.string(from: self)
    }
}


let reminders: [Reminder] = [
    .init(desc: "Reminder 1", isFavorite: false, dueDate: "20230101".asDateYYYYMMDD()),
    .init(desc: "Reminder 2", isFavorite: false, dueDate: "20230201".asDateYYYYMMDD()),
    .init(desc: "Reminder 3", isFavorite: true, dueDate: "20221201".asDateYYYYMMDD()),
    .init(desc: "Reminder 4", isFavorite: true, dueDate: "20230107".asDateYYYYMMDD()),
    .init(desc: "Reminder 5", isFavorite: false, dueDate: "20221101".asDateYYYYMMDD())
]
```

# Step 2 - Prepare the ContentView

Make a `ForEach` list to display an HStack with the value of description, dueDate for each item in the `reminders` array. And in the HStack, the "heart" image is a pink "heart.fill" when the reminder is favorite, and a gray "heart" when it’s not.

```swift
struct ContentView: View {
    var body: some View {
        NavigationView {
            ScrollView {
                ForEach(reminders, id: \.id) { reminder in
                    HStack {
                        VStack {
                           Text(reminder.desc).bold()
                           Text(reminder.dueDate.asStringYYYYMMDD())
                        }
                        Spacer()
                        Image(systemName: reminder.isFavorite ? "heart.fill" : "heart")
                        .foregroundColor(reminder.isFavorite ? .pink : .secondary)
                    }
                    .padding(.horizontal, 25)
                    .padding(.vertical)
                }
            }
            .navigationTitle("Reminders")
        }
    }
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1676428392287/38a27f9b-2fcd-4591-be9d-a3d447c62b51.png align="center")

# Step 3 - Sort the array

In the preview above, the array is displayed in the SwiftUI view, but it doesn’t seem that the order makes any sense. So now let’s sort the array.

Add a private computed property in your content view named `sortedReminders`. Call the function `sorted`. In the closure, we need to compare two reminders and return a boolean value. That being said, if the condition returned by the closure is true, the sorted array will place `reminder1` before `reminder2`.

So let’s see through the closure:

First, if `isFavorite` are the same for both reminders (no matter they are both favorited or not), we need to compare the value of `dueDate`, whoever is lower should come first. Second, if `isFavorite` are different, we just need to put the favorite one first.

This piece of logic goes into the code below.

```swift
struct ContentView: View {
    private var sortedReminders: [Reminder] {
        reminders.sorted { reminder1, reminder2 in
            if reminder1.isFavorite == reminder2.isFavorite {
                return reminder1.dueDate < reminder2.dueDate
            } else {
                return reminder1.isFavorite
            }
        }
    }
    var body: some View {
        //...
    }
}
```

Once done with the computed property, let update the data source of the `ForEach` element from `reminders` to `sortedReminders` .

```swift
ForEach(sortedReminders, id: \.id) { reminder in
```

Now you can see that favorite reminders are shown first, and no matter if an item is favorite or not, it comes first if its due date is nearer that others.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1676428983537/d1cd4aa7-f4e8-45b3-b17e-a7b343a70977.png align="center")

# Step 4 - Shorthand Syntax for the `sort` function

To be honest, the `sort` closure above is quite redundant. Instead, we can have it tidier with a shorthand syntax in Swift.

Now let’s add a new computed property named `sortedRemindersv2` and add `v2` after the array in `ForEach` data source in the view.

This time we need just one line in the closure:

`{ $0.isFavorite == $1.isFavorite ? ($0.dueDate < $1.dueDate) : $0.isFavorite }`

`$0` and `$1` are basically `reminder1` and `reminder2` from Step 3, and there’s no need to write `reminder1, reminder 2 in` now.

And this line is using the ternary operator `bool ? valueIfTrue : valueIfFalse`

`$0.isFavorite == $1.isFavorite` checks if both reminders are/are not favorite. If `isFavorite` are the same for both, this line is going to return `$0.dueDate < $1.dueDate` , which put the nearer due date at the top. If not, the favorite one will come first.

```swift
// new computed property
private var sortedRemindersv2: [Reminder] {
    reminders.sorted { $0.isFavorite == $1.isFavorite ? ($0.dueDate < $1.dueDate) : $0.isFavorite }
}
var body: some View {
    NavigationView {
        ScrollView {
            // remember to update here too!
            ForEach(sortedRemindersv2, id: \.id) { reminder in
```

With the code updated, you should see the same result from Step 3 in the preview.

That’s all for this post, hope you learned how to sort an array of custom objects. Again, Happy Valentine’s Day. Remember to leave a comment and subscribe to my newsletter. I’ll see you all tomorrow!
