Swift: Sort Array by Sort Descriptors

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.

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



Leave a reply



Submit