Is there a stable sort that works with partial orders in the standard library? [duplicate] - 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?

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

I use this wrapper
extension Array where Element: Comparable, Element: AnyObject {
public func stableSorted() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left < right {
return ComparisonResult.orderedAscending
}
if left > right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
public func stableReversed() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left > right {
return ComparisonResult.orderedAscending
}
if left < right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
}

I appreciate the elegance of Tom's answer. Harking back to my Perl days I've reworked it to use ComparisonResult and the spaceship (<=>) operator:
extension Sequence {
func sorted(with comparator: (Element, Element) throws -> ComparisonResult) rethrows -> [Element]
{
return try enumerated()
.sorted { (try comparator($0.element, $1.element) || $0.offset <=> $1.offset) == .orderedAscending }
.map { $0.element }
}
}
extension Comparable {
static func <=> (lhs: Self, rhs: Self) -> ComparisonResult {
if lhs < rhs { return .orderedAscending }
if lhs > rhs { return .orderedDescending }
return .orderedSame
}
}
extension ComparisonResult {
static func || (lhs: Self, rhs: #autoclosure () -> Self) -> ComparisonResult {
if lhs == .orderedSame {
return rhs()
}
return lhs
}
}

Related

How to add filter(_:) method to generic structure in Swift?

I want to add a filter(_:) method to Pyramida structure. It should take a single argument, a closure that: takes an Element and returns a Bool, and return a new Pyramida that contains any elements for which the closure returns true, e.g. values higher than 10.
When I used the same code as per (within exercise) already existent func map also for func filter, its implementation prints out just [false, false, true], however not the values above 10.
Also tried to adjust the func filter as per syntax stated within Developer Documentation, however did not manage it further, too many errors.
struct Pyramida<Element> {
var items = [Element]()
mutating func push(_ newItem: Element) {
items.append(newItem)
}
mutating func pop() -> Element? {
guard !items.isEmpty else {return nil}
return items.removeLast()
}
func map<U>(_ txform: (Element) -> U) -> Pyramida<U> {
var mappedItems = [U]()
for item in items {
mappedItems.append(txform(item))
}
return Pyramida<U>(items: mappedItems)
}
func filter(_ isIncluded: (Element) -> Bool) -> Pyramida<[Int]> {
var filteredItems = [Int]()
for item in items {
if item > 10 {
filteredItems.append(filteredItems(item))
return Pyramida(items: filteredItems)
}
}
}
}
var ints = Pyramida<Int>()
ints.push(2)
ints.push(4)
ints.push(11)
var tripled = ints.map{ 3 * $0 }
print(String(describing: tripled.items))
var aboveTen = ints.filter{ 10 < $0} // code func map<U> for filter<U>
print(String(describing: aboveTen.items))
you have to add this method and if the isIncluded is true you have to append the new element in your array.
func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> Pyramida<Element> {
var newElements: [Element] = []
for number in items where try isIncluded(number) {
newElements.append(number)
}
return Pyramida(items: newElements)
}
and you also can use this.
public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> Pyramida <Element>{
return Pyramida(items: try items.filter(isIncluded))
}

Compare array of tuples in swift

I've declared the following for my tuple type:
public typealias Http2HeaderTableEntry = (field: String, value: String?)
func ==(lhs: [Http2HeaderTableEntry], rhs: [Http2HeaderTableEntry]) -> Bool {
guard lhs.count == rhs.count else { return false }
for (idx, element) in lhs.enumerated() {
guard element.field == rhs[idx].field && element.value == rhs[idx].value else {
return false
}
}
return true
}
But now when I try and use it in an assertion, like XCTAssertEqual(var1, var2) I'm getting a compiler error:
Cannot invoke 'XCTAssertEqual' with an argument list of type '([Http2HeaderTableEntry], [Http2HeaderTableEntry])'
What do I need to do in order to be able to compare two arrays of tuples in Swift 4?
Since tuples can't have conformances yet, there's not really a way to implement this in any general way at this point.
Reference here

Extending SignalProducerType in case Value is an Array<SomeProtocol>

I have a protocol for fetching database objects by PrimaryKey
typealias PrimaryKey = String
protocol PrimaryKeyConvertible {
var pkValue : PrimaryKey { get }
static func pkObject(key: PrimaryKey) -> Self?
}
and I want to extend the SignalProducerType to be able to operate on a SignalProducer.Value of that type.
So the Single object extension (single as in not Array) works fine and implemented as following:
extension SignalProducerType
where Value: PrimaryKeyConvertible
{
func fetchOnMainThread() -> SignalProducer<Value?, Error> {
return
self.map{ (obj: Value) -> PrimaryKey in
return obj.pkValue
}
.observeOn(UIScheduler())
.map{ (key: PrimaryKey) -> Value? in
return Value.pkObject(key)
}
}
}
But when I try to implement it on an Array of these elements i hit some compilation challenges:
extension SignalProducerType
{
func fetchOnMainThread<P: PrimaryKeyConvertible where Self.Value == Array<P>>() -> SignalProducer<[P], Error> { //(1)
return self.map({ (value: Self.Value) -> [PrimaryKey] in
return value.map{ $0.pkValue } //(2)
})
}
}
(1) i suspect that the signature is not communicating the idea to the compiler correctly
(2) produces the following error:
Type of expression is ambiguous without more context
the issue i'm trying to solve is how to let the compiler recognize the the SignalProducer is operating on an Array<P> where P is PrimaryKeyConvertible and have the .map operate on it accordingly ...
my current solution for the array issue is to implement using a generic function as listed below:
func fetchOnMainThread<Value: PrimaryKeyConvertible, Error: ErrorType>
(signal: SignalProducer<[Value], Error>) -> SignalProducer<[Value], Error> {
return signal
.map{ (convertibles: [Value]) -> [PrimaryKey] in
return convertibles.map { $0.pkValue }
}
.observeOn(UIScheduler())
.map{ (keys: [PrimaryKey]) -> [Value] in
return keys.flatMap{ Value.pkObject($0) }
}
}
and then used for example:
extension GoogleContact: PrimaryKeyConvertible {...}
extension GoogleContact {
static func fetchGoogleContactsSignal() -> SignalProducer<[GoogleContact], GoogleContactError> { ...}
}
and the call site would be like:
let signal = fetchOnMainThread(GoogleContacts.fetchGoogleContactsSignal()).onNext...
where I would prefer to have it as an extension where it would flow as usual
GoogleContacts
.fetchGoogleContactsSignal()
.fetchOnMainThread()
Update
another version of the function I've tried : (#J.Wang)
extension SignalProducerType
where Value == [PrimaryKeyConvertible]
{
func fetchArrayOnMainThread2<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
return self
.map{ (values: Self.Value) -> [PrimaryKey] in
return values.map{ $0.pkValue }
}
.deliverOnMainThread()
.map{ (keys: [PrimaryKey]) -> [T] in
return keys.flatMap{ T.pkObject($0) }
}
}
}
let signal =
GoogleContacts
.fetchGoogleContactsSignal()
.fetchArrayOnMainThread2() //(3)
(3) Generates error:
'[PrimaryKeyConvertible]' is not convertible to '[GoogleContact]'
Hmm, although I'm not quite sure what the problem is, but I think the following implementation might be what you want.
extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
func fetchOnMainThread() -> SignalProducer<[PrimaryKey], Error> {
return self.map { value in
value.map { $0.pkValue }
}
}
}
Try this:
extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
func fetchOnMainThread<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
return self.map { value in
value.map { $0.pkValue }
}.map { keys in
keys.flatMap { T.pkObject($0) }
}
}
}

Defining `Comparable` for optional types in Swift 1.2

import SwiftyJSON
public typealias FeedItem = JSON
extension FeedItem {
var id: Int { get { return self["id"].intValue } }
}
public func <(lhs: FeedItem, rhs: FeedItem) -> Bool {
return lhs.id < rhs.id
}
public func ==(lhs: FeedItem, rhs: FeedItem) -> Bool {
return lhs.id == rhs.id
}
extension FeedItem:Comparable {}
extension Optional : Comparable {}
public func < <T>(l:T?, r:T?) -> Bool {
if let a=l,b=r {
return a < b
} else if (l==nil) && (r != nil) { return true }
else { return false }
}
public func == <T>(l:T?, r:T?) -> Bool {
if let a=l, b=r {
return a==b
} else {
return false
}
}
First, it should read extension Optional<T where T:Comparable>: Comparable but swift 1.2 does not allow that. Anyway I can express the constraint more explicitly rather than expecting the reader to realize the fact by noticing return a < b and return a==b ?
Second (and apparently more important): the code above works when I use < and > but minElement and maxElement both return nil when a nil is present in their input no matter what and min and max fall into infinite recursion:
let items: [FeedItem?] = [ nil, .Some(JSON(["id":2])), .Some(JSON(["id":1])) ]
println(items[0]?.id) // nil
println(items[1]?.id) // Optional(2)
println(items[2]?.id) // Optional(1)
println(items[0] < items[1]) // true
println(items[1] < items[2]) // false
println(items[2] < items[1]) // true
println(minElement(items)) // nil
println(maxElement(items)) // nil
println( min(items[0],items[2]) ) // nil
println( min(items[2],items[1]) ) // crashes due to infinite recursion
I'm no debug (or swift) expert but from what I can gather in XCode I believe the
if let a=l,b=r {
return a < b
}
part somehow misses the point that a and b are not Optionals but FeedItems. I expect the a < b should call the < operator on FeedItem and not the one on Optional but apparently this is exactly what happens; i.e. a < b resolves to the same function it is called from (that is, the < for Optionals) and thus a recursion happens. I might be wrong though.
Insights ?

Comparing objects in an Array extension causing error in Swift

I'm trying to build an extension that adds some of the convenience functionality of NSArray/NSMutableArray to the Swift Array class, and I'm trying to add this function:
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
But unfortunately, this line:
if object == objectToCompare {
Is giving the error:
could not find an overload for '==' that accepts the supplied arguments
Question
What am I doing wrong to cause this error?
Example
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
}
Actually there is no need to implement indexOfObject:; there is a global function find(array, element) already.
You can always create an extension that uses NSArray's indexOfObject, e.g:
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
return (self as NSArray).indexOfObject(object)
}
}
You can specify that your array items can be compared with the <T : Equatable> constraint, then you can cast your object into T and compare them, e.g:
extension Array {
func indexOfObject<T : Equatable>(o:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
let to = objectToCompare as T
if o == to {
return idx
}
}
}
return nil
}
}
My guess is that you have to do something like this:
func indexOfObject<T: Equatable>(object: T) -> Int? {
and so on.
Here's a relevant example from Apple's "The Swift Programming Language" in the "Generics" section:
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
The key idea here is that both value and valueToFind must of a type that is guaranteed to have the == operator implemented/overloaded. The <T: Equatable> is a generic that allows only objects of a type that are, well, equatable.
In your case, we would need to ensure that the array itself is composed only of objects that are equatable. The Array is declared as a struct with a generic <T> that does not require it to be equatable, however. I don't know whether it is possible to use extensions to change what kind of types an array can be composed of. I've tried some variations on the syntax and haven't found a way.
You can extract the compare part to another helper function, for example
extension Array {
func indexOfObject(object: T, equal: (T, T) -> Bool) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if equal(object, objectToCompare) {
return idx
}
}
}
return nil
}
}
let arr = [1, 2, 3]
arr.indexOfObject(3, ==) // which returns {Some 2}
You were close. Here's a working extension:
extension Array {
func indexOfObject<T: Equatable>(object:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare as T {
return idx
}
}
}
return nil
}
}
Swift had no way of knowing if object or objectToCompare were equatable. By adding generic information to the method, we're then in business.