I've been using the sort() function but it mixes up the relative order.
This is how my code looks.
recipes.sort { $0.skill.value <= $1.skill.value }
Swift API says that:
The sorting algorithm is not stable. A nonstable sort may change the relative order of elements that compare equal.
How can I change this so that the relative order stays the same as before?
4 If you want to sort your array in ascending order then use below syntax: var arrayName = sorted (arrayName, <) as the sorted () is the predefined function in swift and < is used to indicate that the array should be sorted in ascending order.
Any method that can be used with Objective-C sortedArrayUsingSelector: can be used with Swift sort (or sorted) provided the type of thing in the array is known. So, in your code: Show activity on this post.
If you try to run a simple sort on the personArray defined above, the Swift compiler communicates that it (paraphrasing) does not know how to compare the items in the array, We need to get the Person type to conform to Comparable. Another is just use the sort function to tell Swift how to sort the elements (and this guide is to do the latter).
In Swift 2, the functions have different names. Swift 2: sort () -> Swift 3: sorted () Swift 2: sortInPlace () -> Swift 3: sort () For more details about the changed API naming, you can take a look at this Swift evolution proposal: Apply API Guidelines to the Standard Library.
The implementation below just work like the sorted
method in the standard library, without additional limit.
extension RandomAccessCollection {
/// return a sorted collection
/// this use a stable sort algorithm
///
/// - Parameter areInIncreasingOrder: return nil when two element are equal
/// - Returns: the sorted collection
public func stableSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {
let sorted = try enumerated().sorted { (one, another) -> Bool in
if try areInIncreasingOrder(one.element, another.element) {
return true
} else {
return one.offset < another.offset
}
}
return sorted.map { $0.element }
}
}
A stable sort needs to preserve the original order. So we give every element a weight of order besides its value, the index, then the original sort method will just work, as there will never be 2 equal elements.
I appreciate the elegance of leavez's answer. I adapted it to have the same signature as Sequence.sorted(by:)
:
extension Sequence {
func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool)
rethrows -> [Element]
{
return try enumerated()
.sorted { a, b -> Bool in
try areInIncreasingOrder(a.element, b.element) ||
(a.offset < b.offset && !areInIncreasingOrder(b.element, a.element))
}
.map { $0.element }
}
}
let sortedArray = (recipes as NSArray).sortedArray(options: .stable, usingComparator: { (lhs, rhs) -> ComparisonResult in
let lhs = (lhs as! Recipe)
let rhs = (rhs as! Recipe)
if lhs.skill.value == rhs.skill.value {
return ComparisonResult.orderedSame
} else if lhs.skill.value < rhs.skill.value {
return ComparisonResult.orderedAscending
} else {
return ComparisonResult.orderedDescending
}
})
Took from here: https://medium.com/@cocotutch/a-swift-sorting-problem-e0ebfc4e46d4
In Swift 5 sort()
uses stable implementation and soon it will become officially guaranted to be stable.
From Swift forums:
...
On the other hand, the actual implementation calls/// Sorts the elements of this buffer according to `areInIncreasingOrder`, /// using a stable, adaptive merge sort. /// /// The adaptive algorithm used is Timsort, modified to perform a straight /// merge of the elements using a temporary buffer. @inlinable public mutating func _stableSortImpl( by areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows { ... }
And
If I recall, sort() is currently stable, but it is not yet guaranteed to be stable (meaning, the fact that it is stable is currently an implementation detail, and a future version of Swift could ship an unstable algorithm instead).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With