Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stable sort an array in swift?

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?

like image 442
demiculus Avatar asked Nov 18 '16 09:11

demiculus


People also ask

How to sort array in ascending order in Swift?

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.

What methods can be used with Swift sort?

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.

How to sort a personarray in Swift?

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).

What is the difference between Swift 2 and Swift 3 sort functions?

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.


4 Answers

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.

like image 73
leavez Avatar answered Sep 27 '22 19:09

leavez


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 }
  }
}
like image 33
Tom Avatar answered Sep 27 '22 18:09

Tom


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

like image 36
Esqarrouth Avatar answered Sep 27 '22 17:09

Esqarrouth


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).

like image 39
kelin Avatar answered Sep 27 '22 17:09

kelin