Swift - Sort Array of Objects With Multiple Criteria

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 Contacts 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



Leave a reply



Submit