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.
Sorting a Custom Object by different property values in Swift
What I would go with, is using KeyPaths:
func sortCustomers<T: Comparable>(customers: [Customer], with itemPath: KeyPath<Customer, T>) -> [Customer] {
return customers.sorted() {
$0[keyPath: itemPath] < $1[keyPath: itemPath]
}
}
This approach avoids the need for your enum at all, and allows you to just do
let testData = [Customer(name: "aaaa", isActive: false, outstandingAmount: 1, customerGroup: "aaa"),
Customer(name: "bbbb", isActive: true, outstandingAmount: 2, customerGroup: "bbb")];
let testResultsWithName = sortCustomers(customers: testData, with: \Customer.name)
let testResultsWithActive = sortCustomers(customers: testData, with: \Customer.isActive)
// etc
Notice that I switched the >
to a <
. That is the default expectation and will result in "a" before "b", "1" before "2", etc.
Also, you need to add an extension for Bool to be comparable:
extension Bool: Comparable {
public static func <(lhs: Bool, rhs: Bool) -> Bool {
return lhs == rhs || (lhs == false && rhs == true)
}
}
To round out the approach, you can also pass in a comparison function:
func sortCustomers<T: Comparable>(customers: [Customer], comparing itemPath: KeyPath<Customer, T>, using comparitor: (T, T) -> Bool) -> [Customer] {
return customers.sorted() {
comparitor($0[keyPath: itemPath], $1[keyPath: itemPath])
}
}
let testResults = sortCustomers(customers: testData, comparing: \Customer.name, using: <)
This way you can use the normal comparison operators: (<, <=, >, >=) as well as a closure if you want custom sorting.
Swift how to sort array dictionary by key values
You could use higher-order functions sorted and flatMap nested together like so:
let sortedURLs = dictionaryArray
.sorted(by: { $0.keys.first! > $1.keys.first! })
.flatMap({ $0.values.first! })
Note: watch out of forced unwrapped optionals it is working here but could lead to an exception
Sort NSSortDescriptor by entity index Swift
In iOS there is no such field as 'self' for a table record. You must create a 'date' or 'id' field if you wish to sort by date or id.
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
}
Related Topics
Using Avaudioplayer to Play Remote Mp3 File in Swift
Dynamic Uitablecellview Height
Delegates: Pass Data Without Segue
iOS PDFkit Displaymode = Singlepage Only Shows the First Page of the PDF
Stream Data from Network in Avaudioengine, Is It Possible
Viewing Os_Log Messages in Device Console
Ios: How to Create Expandable Tableview in Swift Without Using Third Party Libraries or Pods
Is Weak Self Needed for Table View Cell Button Closure
Increase the Width of Content View Programmatically in Swift
iPhone Simulator Plays Video, Real Device Won'T
Live Render Iboutlet Connected Subviews via Ibinspectable Properties
How to Create an Image of Specific Size from Uiview
Scene Kit Memory Management Using Swift
Swift3 Random Extension Method
Facebook Sdk Login Throws Error in Swift 2 iOS 9
Swift App Crashes on Real Device But Works on Simulator
Viewcontroller.Type Does Not Have a Member Names 'Answerone' - Swift