I have simple code:
struct User {
let id: Int
let name: String
}
var users: [User] = [User(id:1, name:"Alpha"), User(id:2, name:"Beta"), User(id:3, name:"Gamma")]
print(users)
users.sort { $0.name > $1.name }
print(users)
What is the best way of changing the sorting field based on variable? I mean, I want to have some variable like "sortBy" which contains value of sorting field ("id", "name" etc, struct may contain dozens of fields). And I can't find out the proper way of doing so in Swift.
Pseudo-code:
var sortBy = "name"
users.sort { $0.{sortBy} > $1.{sortBy} }
CodePudding user response:
Use key paths and methods.
users.sort(by: \.name)
users.sort(by: \.id)
extension Array where Element == User {
mutating func sort<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable
) rethrows {
self = try sorted(by: comparable, >)
}
}
public extension Sequence {
/// Sorted by a common `Comparable` value, and sorting closure.
func sorted<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable,
_ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
) rethrows -> [Element] {
try sorted {
try areInIncreasingOrder(comparable($0), comparable($1))
}
}
}
CodePudding user response:
You could define a array of predicates to sort over in the order you want:
import Foundation
struct User {
let id: Int
let name: String
}
var users: [User] = [User(id:3, name:"Gamma"), User(id:1, name:"Zeta"), User(id:1, name:"Alpha"), User(id:4, name:"Alpha"), User(id:2, name:"Beta")]
print("Before Sorting: \(users)")
typealias AreInIncreasingOrder = (User, User) -> Bool
users.sort { (lhs, rhs) in
let predicates: [AreInIncreasingOrder] = [
{ $0.id < $1.id },
{ $0.name < $1.name}
]
for predicate in predicates {
if !predicate(lhs, rhs) && !predicate(rhs, lhs) {
continue
}
return predicate(lhs, rhs)
}
return false
}
print("After Sorting: \(users)")
Output:
Before Sorting: [Page_Contents.User(id: 3, name: "Gamma"), Page_Contents.User(id: 1, name: "Zeta"), Page_Contents.User(id: 1, name: "Alpha"), Page_Contents.User(id: 4, name: "Alpha"), Page_Contents.User(id: 2, name: "Beta")]
After Sorting: [Page_Contents.User(id: 1, name: "Alpha"), Page_Contents.User(id: 1, name: "Zeta"), Page_Contents.User(id: 2, name: "Beta"), Page_Contents.User(id: 3, name: "Gamma"), Page_Contents.User(id: 4, name: "Alpha")]