Как отсортировать массив пользовательских объектов по значению свойства в Swift?
Основные способы сортировки массивов пользовательских объектов в Swift
В Swift есть два основных подхода для сортировки массивов: мутирующий метод sort() (изменяет исходный массив) и немутирующий метод sorted() (возвращает новый отсортированный массив). Оба принимают замыкание, в котором вы определяете порядок сортировки.
1. Сортировка по одному свойству (убывание)
Чтобы отсортировать массив по убыванию приоритета, используйте оператор > вместо <:
let sortedByPriorityDesc = tasks.sorted(by: { $0.priority > $1.priority })
// Результат: сначала priority = 3, затем 2, затем 1
2. Множественная сортировка (по приоритету, затем по дате)
Для сортировки по нескольким критериям используйте последовательные проверки в замыкании. Сначала сравниваем приоритеты (по убыванию), если они равны — сравниваем даты (по возрастанию):
let sortedTasks = tasks.sorted { first, second in
if first.priority != second.priority {
return first.priority > second.priority // по убыванию приоритета
} else {
return first.dueDate < second.dueDate // по возрастанию даты
}
}
3. Использование кортежей для краткой записи
Swift позволяет сравнивать кортежи поэлементно. Это делает код очень компактным:
let sortedTasks = tasks.sorted {
($0.priority, $0.dueDate) > ($1.priority, $1.dueDate)
}
Но внимание: такое сравнение работает только если все элементы кортежа соответствуют протоколу Comparable. В данном случае priority (Int) и dueDate (Date) — Comparable, поэтому код корректен. Если нужно смешивать направления сортировки (одно по возрастанию, другое по убыванию), используйте явные замыкания из пункта 2.
4. Сортировка с помощью ключевых путей (Key Paths) — Swift 5.6+
Начиная с Swift 5.6, вы можете использовать ключевые пути для ещё более лаконичной записи. Создайте массив сортировок:
import Foundation
// Определяем порядок сортировки
let sortDescriptors: [SortDescriptor] = [
SortDescriptor(\Task.priority, order: .reverse), // по убыванию
SortDescriptor(\Task.dueDate, order: .forward) // по возрастанию
]
// Применяем
let sortedTasks = tasks.sorted(using: sortDescriptors)
5. Альтернатива: реализация протокола Comparable
Если вы часто сортируете объекты одного типа, можно сделать структуру соответствующей протоколу Comparable:
struct Task: Comparable {
let title: String
var priority: Int
var dueDate: Date
static func < (lhs: Task, rhs: Task) -> Bool {
if lhs.priority != rhs.priority {
return lhs.priority > rhs.priority // убывание
} else {
return lhs.dueDate < rhs.dueDate // возрастание
}
}
}
// Теперь можно сортировать просто так:
let sortedTasks = tasks.sorted() // по умолчанию использует оператор <
Важные замечания:
- Метод
sort()изменяет исходный массив,sorted()— нет. Выбирайте по ситуации. - Замыкание должно возвращать
true, если первый элемент должен идти перед вторым. - Для сортировки по возрастанию используйте
<, по убыванию —>. - Если свойство опционально (например,
dueDate: Date?), нужно обрабатывать nil. Обычно nil помещают либо в начало, либо в конец.
Выберите подход, который лучше всего подходит под ваш стиль кода. Для простых случаев достаточно замыкания с кортежами, для сложных — реализация Comparable или SortDescriptor.
<<Сортировка с использованием NSSortDescriptor (для совместимости с Objective-C)
Если вы работаете с классами, унаследованными от NSObject, или вам нужно передавать сортировку как данные, можно использовать NSSortDescriptor из Foundation:
import Foundation
class TaskObj: NSObject {
@objc let title: String
@objc var priority: Int
@objc var dueDate: Date
init(title: String, priority: Int, dueDate: Date) {
self.title = title
self.priority = priority
self.dueDate = dueDate
}
}
let tasksObj = [
TaskObj(title: "Купить продукты", priority: 3, dueDate: Date()),
TaskObj(title: "Сделать отчет", priority: 1, dueDate: Date().addingTimeInterval(86400))
]
let sortDesc1 = NSSortDescriptor(key: #keyPath(TaskObj.priority), ascending: false)
let sortDesc2 = NSSortDescriptor(key: #keyPath(TaskObj.dueDate), ascending: true)
let sortedArray = (tasksObj as NSArray).sortedArray(using: [sortDesc1, sortDesc2]) as! [TaskObj]
Этот способ удобен, когда сортировка задаётся динамически (например, из пользовательского интерфейса), но требует наследования от NSObject и аннотаций @objc. Для чистых Swift-структур лучше использовать методы из первого ответа.
Ответов пока нет.
Войдите чтобы оставить ответ