Swift - Sort array of objects with multiple criteria
Think of what "sorting by multiple criteria" means. It means that two objects are first compared by one criteria. Then, if those criteria are the same, ties will be broken by the next criteria, and so on until you get the desired ordering.
let sortedContacts = contacts.sort {
if $0.lastName != $1.lastName { // first, compare by last names
return $0.lastName < $1.lastName
}
/* last names are the same, break ties by foo
else if $0.foo != $1.foo {
return $0.foo < $1.foo
}
... repeat for all other fields in the sorting
*/
else { // All other fields are tied, break ties by last name
return $0.firstName < $1.firstName
}
}
What you're seeing here is the Sequence.sorted(by:)
method, which consults the provided closure to determine how elements compare.
If your sorting will be used in many places, it may be better to make your type conform to the Comparable
protocol. That way, you can use Sequence.sorted()
method, which consults your implementation of the Comparable.<(_:_:)
operator to determine how elements compare. This way, you can sort any Sequence
of Contact
s without ever having to duplicate the sorting code.
Sort array of objects by two properties
Yes there is a very simple way using the Array.sort()
Code:
var sorted = array.sorted({ (s1, s2) -> Bool in
if s1.isPriority && !s2.isPriority {
return true //this will return true: s1 is priority, s2 is not
}
if !s1.isPriority && s2.isPriority {
return false //this will return false: s2 is priority, s1 is not
}
if s1.isPriority == s2.isPriority {
return s1.ordering < s2.ordering //if both save the same priority, then return depending on the ordering value
}
return false
})
The sorted array:
true - 10
true - 10
true - 12
true - 12
true - 19
true - 29
false - 16
false - 17
false - 17
false - 17
false - 18
Another a bit shorter solution:
let sorted = array.sorted { t1, t2 in
if t1.isPriority == t2.isPriority {
return t1.ordering < t2.ordering
}
return t1.isPriority && !t2.isPriority
}
Swift: sort array by multiple criteria (weights)
You haven't got condition for c1.unreadMessages < c2.unreadMessages
that should cover all requirements:
let sorted = chats.sort({ (c1, c2) -> Bool in
if c1.unreadMessages > c2.unreadMessages {
return true
} else if c1.unreadMessages < c2.unreadMessages {
return false
} else if c1.messages > c2.messages {
return true
}
return false
})
Swift - Sort array by optionals
If I understand you properly, you have this kind of structure:
struct Item {
let isChecked: Bool
let creationDate: Date
let dueDate: Date?
}
and I think by "closest" you mean that you want the items sorted by being close to a specific Date
.
This could be done in the following manner. Let's define a helper method first:
extension Item {
func getOrderingClosestTo(_ date: Date) -> (Int, Int, TimeInterval) {
return (
// items that are not checked are "smaller", they will be first
isChecked ? 1 : 0,
// items with dueDate are smaller, they will be first
dueDate != nil ? 0 : 1,
// items closer to "date" will be smaller
abs((dueDate ?? creationDate).timeIntervalSince(date))
)
}
}
Then you can call:
let myDate: Date = ...
let items: [Item] = ...
let sortedItems = items.sorted { $0.getOrderingClosestTo(myDate) < $1.getOrderingClosestTo(myDate) }
sort by two properties
To sort it by two factors you can do your custom logic with the "sorted" method :
Here is an example that you can test in a playground.
struct MyStruct {
let section: String
let order: String
}
let array = [MyStruct(section: "section1", order: "1"),
MyStruct(section: "section2", order: "1"),
MyStruct(section: "section1", order: "2"),
MyStruct(section: "section2", order: "2")]
let sortedArray = array.sorted { (struct1, struct2) -> Bool in
if (struct1.section != struct2.section) { // if it's not the same section sort by section
return struct1.section < struct2.section
} else { // if it the same section sort by order.
return struct1.order < struct2.order
}
}
print(sortedArray)
Sort array in swiftui by multiple conditions
Here is a simple way to sort on multiple properties (I have assumed a sort order here for each property since it isn't mentioned in the question)
let sorted = users.sorted {
if $0.isVip == $1.isVip {
if $0.age == $1.age {
return $0.userCountry < $1.userCountry
} else {
return $0.age < $1.age
}
}
return $0.isVip && !$1.isVip
}
If the above is the natural sort order for the type then you could let the type implement Comparable
and implement the <
func
struct UserInformationModel: Identifiable, Hashable, Comparable {
//properties removed for brevity
static func < (lhs: UserInformationModel, rhs: UserInformationModel) -> Bool {
if lhs.isVip == rhs.isVip {
if lhs.age == rhs.age {
return lhs.userCountry < rhs.userCountry
} else {
return lhs.age < rhs.age
}
}
return lhs.isVip && !rhs.isVip
}
}
and then the sorting would be
let sorted = users.sorted()
How do I sort Swift objects by multiple criteria
Seems like it might be a good idea to add a dateCount
computed property to your DateRange
type. This would be a good time for pattern matching:
extension DateRange {
// returns the number of non-nil NSDate members in 'from' and 'to'
var dateCount: Int {
switch (from, to) {
case (nil, nil): return 0
case (nil, _): return 1
case (_, nil): return 1
default: return 2
}
}
}
Then you can sort your list with a simple closure:
var ranges = [DateRange(nil, nil), DateRange(NSDate(), nil), DateRange(nil, NSDate()), DateRange(nil, nil), DateRange(NSDate(), NSDate())]
ranges.sort { $0.dateCount > $1.dateCount }
If you wanted, you could even make it Comparable
with a few more lines:
extension DateRange : Comparable { }
func ==(lhs: DateRange, rhs: DateRange) -> Bool {
return lhs.dateCount == rhs.dateCount
}
func <(lhs: DateRange, rhs: DateRange) -> Bool {
return lhs.dateCount > rhs.dateCount
}
This lets you sort your list properly with an operator argument:
ranges.sort(<)
Related Topics
Rounding a Double Value to X Number of Decimal Places in Swift
How to Cast Self to Unsafemutablepointer≪Void≫ Type in Swift
Strange Swift Numbers Type Casting
Why My Return Is Nil But If I Press the Url in Chrome/Safari, I Can Get Data
How to Encode a String to Base64 in Swift
Changing Navigation Title Programmatically
Weak References in Swift Playground Don't Work as Expected
Nsfilemanager Fileexistsatpath:Isdirectory and Swift
How to Convert Double to Int in Swift
Alternative to Performselector in Swift
Formatting Input For Currency With Nsnumberformatter in Swift
Unexpected Non-Void Return Value in Void Function (Swift 2.0)
Choosing Coredata Entities from Form Picker