Categories
iOS Development

How to sort an array of objects by date property

You might know the very handy sort(by:) method in Swift that you can use to sort any kind of array. If not, see this as some kind of friendly introduction to you. Read carefully and I will teach you now how to sort an array of custom type objects by their date property 🙂

The sort(by:) method is pretty well documented in Apple’s official documentation: https://developer.apple.com/documentation/swift/array/2296801-sort

An image of tea bags sorted by color.
This has actually nothing to do with sorting something by date but these teabags are nicely sorted anyway 😅

In a lot of cases you just call someArray.sort(). This will sort an array of elements conforming to Comparable (e.g. Int, String and a lot of other type) in ascending order. If you want to sort it in ascending order you can just call someArray.sort(by: >). However, what if you have an array of some custom type that does not conform to Comparable and you want to sort this array by a date property this class has?

A ComparisonResult can be .orderedDescending, .orderedAscending or .orderedSame. In order to sort our array of objects, we can just check these values.

So how do we sort our array of objects by their date property? You can either pass in your sorting logic directly to the sort(by:) method or make your custom type conform to Comparable (which is probably the cleaner way).

The documentation for sort(by:) says that you can basically do anything in the passed in closure to compare your array. So we can just do that to compare our two date properties. Thankfully, when using Date, you get a comparison method for free. It is called Date.compare(_:) and it’s great. You pass in two dates and get back a ComparisonResult. This makes your code very readable without doing anything because ComparisonResult can be either .orderedAscending, .orderedDescending or .orderedSame.

In the following you can see some examples for passing in your logic directly.

myElements.sort(by: { element1, element2 in let comparisonResult = element1.date.compare(element2.date) return comparisonResult == .orderedDescending })
Code language: Swift (swift)

or if you prefer the shorter syntax using $:

contentForMonth.sort(by: { let comparisonResult = $0.date.compare($1.date) return comparisonResult == .orderedDescending })
Code language: Swift (swift)

or even shorter:

someArray.sort(by: { $0.date.compare($1.date) == .orderedDescending })
Code language: Swift (swift)

Conforming to Comparable to easily sort an array of objects by date

Making a custom type conform to Comparable is actually easier than you might think. You just have to define two protocol methods for that. Via the first method you define in which case one object can be seen as less than a second object. The second method is for defining equality of two objects.

The benefit of going this way is that you can sort your array of custom type objects anywhere in your Swift project without passing in sorting logic anymore.

In the following you see an implementation for a custom type RowModel that has an optional Date property. In this case, I added an additional method for unwrapping and comparing the two arguments. If your date property is not optional you would not need that.

extension RowModel: Comparable { static func < (lhs: RowModel, rhs: RowModel) -> Bool { if let result = compare(lhs, rhs), result == .orderedAscending { return true } return false } static func == (lhs: RowModel, rhs: RowModel) -> Bool { if let result = compare(lhs, rhs), result == .orderedSame { return true } return false } fileprivate static func compare(_ lhs: RowModel, _ rhs: RowModel) -> ComparisonResult? { if let lhsDate = lhs.date, let rhsDate = rhs.date { return lhsDate.compare(rhsDate) } return nil } }
Code language: Swift (swift)

With this small addition you can shorten your calls for sorting an array to this:

// Sort for descending order someArray.sort(>) // Sort for ascending order someArray.sort()
Code language: JavaScript (javascript)

Leave a Reply

Your email address will not be published. Required fields are marked *