Como ordenar um array de objetos por propriedade em Swift: olhando sob o capô
Olá, colega! Se você já escreveu um aplicativo em Swift que exibe uma lista de usuários, produtos ou tarefas, certamente já se deparou com a necessidade de ordenar esses dados. A pergunta "How to sort an array of custom objects by property value?" é uma das mais populares no Stack Overflow. E não é à toa: à primeira vista parece simples, mas quando você se aprofunda, um mundo de possibilidades se abre.
Hoje não vamos apenas dar uma resposta pronta. Vamos analisar como a ordenação funciona sob o capô do Swift, quais mecanismos estão envolvidos e como escrever um código não apenas funcional, mas eficiente e elegante. Vamos nessa!
O que é ordenação de array de objetos em Swift?
Imagine: você tem um array de objetos User. Cada usuário tem nome, idade e avaliação. Você precisa exibi-los na tela, ordenados por idade (do mais novo para o mais velho).
Em Swift, isso é feito através dos métodos nativos sort() (ordena o array no local) e sorted() (retorna um novo array ordenado). Ambos aceitam uma closure que define a ordem.
struct User { let name: String let age: Int let rating: Double}
let users = [ User(name: "Ana", age: 25, rating: 4.5), User(name: "Bruno", age: 30, rating: 3.8), User(name: "Carla", age: 22, rating: 4.9)]
// Ordenação por idade (crescente)let sortedByAge = users.sorted { $0.age < $1.age }print(sortedByAge.map { $0.name }) // ["Carla", "Ana", "Bruno"]Pronto. Mas vamos entender o que realmente está acontecendo aqui.
Como o sort() funciona sob o capô: algoritmos e closures
O método sort() em Swift usa o algoritmo híbrido TimSort (nomeado em homenagem a Tim Peters). É um algoritmo de ordenação adaptativo e estável que combina merge sort e insertion sort. Ele é especialmente eficiente em dados parcialmente ordenados — olá, feeds de notícias e listas de tarefas!
A closure que você passa é um predicado de ordenação: uma função que recebe dois elementos e retorna true se o primeiro deve vir antes do segundo. O Swift chama essa closure repetidamente, comparando pares de elementos.
Importante: se seus objetos estiverem em conformidade com o protocolo Comparable, você pode até mesmo dispensar a closure:
struct User: Comparable { let name: String let age: Int static func < (lhs: User, rhs: User) -> Bool { return lhs.age < rhs.age }}
let sortedUsers = users.sorted() // agora simplesMas e se você precisar ordenar por diferentes propriedades em tempo de execução? É aí que entram os key paths (KeyPath).
Ordenação com KeyPath: elegância e flexibilidade
A partir do Swift 4, temos o KeyPath. É uma forma segura de referenciar uma propriedade de um objeto. Combinando KeyPath com closures, podemos escrever uma função de ordenação universal:
extension Array { mutating func sort<T: Comparable>(by keyPath: KeyPath<Element, T>, ascending: Bool = true) { sort { ascending ? $0[keyPath: keyPath] < $1[keyPath: keyPath] : $0[keyPath: keyPath] > $1[keyPath: keyPath] } } func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>, ascending: Bool = true) -> [Element] { return sorted { ascending ? $0[keyPath: keyPath] < $1[keyPath: keyPath] : $0[keyPath: keyPath] > $1[keyPath: keyPath] } }}
// Uso:users.sort(by: \\.age) // por idade (crescente)let sortedByRating = users.sorted(by: \\.rating, ascending: false) // por avaliação (decrescente)Que beleza, não? Type-safe