Skip to content

Instantly share code, notes, and snippets.

@sketchytech
Last active March 7, 2016 16:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sketchytech/5a8f264c4195d888692c to your computer and use it in GitHub Desktop.
Save sketchytech/5a8f264c4195d888692c to your computer and use it in GitHub Desktop.
Swift: A means of secondary sorting with error handling
import UIKit
import Foundation
// stable sort taken from AirspeedVelocity blog
// http://airspeedvelocity.net/2016/01/10/writing-a-generic-stable-sort/
extension RangeReplaceableCollectionType
where
Index: RandomAccessIndexType,
SubSequence.Generator.Element == Generator.Element,
Index.Distance == Index.Stride {
public mutating func stableSortInPlace(
isOrderedBefore: (Generator.Element,Generator.Element)->Bool
) {
let N = self.count
var aux: [Generator.Element] = []
aux.reserveCapacity(numericCast(N))
func merge(lo: Index, _ mid: Index, _ hi: Index) {
aux.removeAll(keepCapacity: true)
var i = lo, j = mid
while i < mid && j < hi {
if isOrderedBefore(self[j],self[i]) {
aux.append(self[j])
j += 1
}
else {
aux.append(self[i])
i += 1
}
}
aux.appendContentsOf(self[i..<mid])
aux.appendContentsOf(self[j..<hi])
self.replaceRange(lo..<hi, with: aux)
}
var sz: Index.Distance = 1
while sz < N {
for lo in startIndex.stride(to: endIndex-sz, by: sz*2) {
merge(lo, lo+sz, lo.advancedBy(sz*2,limit: endIndex))
}
sz *= 2
}
}
}
// my code starts here
enum SortType {
case Ascending
case Descending
}
enum SortError:ErrorType {
case ArrayLengthsNotEqual
}
extension RangeReplaceableCollectionType where Generator.Element: Comparable {
func secondarySort<A>(arr2:[A], type:SortType = .Ascending, stableSort:Bool = true) throws -> [A] {
guard self.count as? Int == arr2.count else {
throw SortError.ArrayLengthsNotEqual
}
// instantiate a Zip2Sequence instance
let zipped = zip(self, arr2)
if stableSort {
var arr = Array(zipped)
if type == .Ascending {arr.stableSortInPlace({$0.0 < $1.0})}
else {arr.stableSortInPlace({$0.0 > $1.0})}
return arr.map({$0.1})
}
else {
let sortedZip = zipped.sort({
if type == .Ascending {return $0.0 < $1.0}
else {return $0.0 > $1.0}
})
return sortedZip.map({$0.1})
}
}
func secondarySortInPlace<A>(inout arr2:[A], type:SortType = .Ascending, stableSort:Bool = true) throws {
arr2 = try self.secondarySort(arr2)
}
}
enum SortBy {
case IntegerArray([Int]), DoubleArray([Double]), FloatArray([Float]), CGFloatArray([CGFloat]), StringArray([String])
init?<S: SequenceType>(arr: S) {
if arr is [Int] {
self = SortBy.IntegerArray(arr as! [Int])
}
else if arr is [Double] {
self = SortBy.DoubleArray(arr as! [Double])
}
else if arr is [Float] {
self = SortBy.FloatArray(arr as! [Float])
}
else if arr is [CGFloat] {
self = SortBy.CGFloatArray(arr as! [CGFloat])
}
else if arr is [String] {
self = SortBy.StringArray(arr as! [String])
}
else {
return nil
}
}
func sort<A>(arr:Array<A>, type:SortType = .Ascending, stableSort:Bool = true) throws -> Array<A> {
switch self {
case IntegerArray(let i):
return try i.secondarySort(arr, type: type, stableSort:stableSort)
case DoubleArray(let d):
return try d.secondarySort(arr, type: type, stableSort:stableSort)
case FloatArray(let f):
return try f.secondarySort(arr, type: type, stableSort:stableSort)
case CGFloatArray(let cg):
return try cg.secondarySort(arr, type: type, stableSort:stableSort)
case StringArray(let str):
return try str.secondarySort(arr, type: type, stableSort:stableSort)}
}
func sortInPlace<A>(inout arr:Array<A>, type:SortType = .Ascending, stableSort:Bool = true) throws {
switch self {
case IntegerArray(let i):
try i.secondarySortInPlace(&arr)
case DoubleArray(let d):
try d.secondarySortInPlace(&arr, type: type, stableSort:stableSort)
case FloatArray(let f):
try f.secondarySortInPlace(&arr, type: type, stableSort:stableSort)
case CGFloatArray(let cg):
try cg.secondarySortInPlace(&arr, type: type, stableSort:stableSort)
case StringArray(let str):
try str.secondarySortInPlace(&arr, type: type, stableSort:stableSort)}
}
}
// data
var arr1 = [1,3,9,3,4,5,6,7,8]
var arr2 = ["A","C","B","X","C","D","E","F","G"]
var arr3 = [2.3,5.2,0.7,2.3,10.6,7.9,2.3,6.4,15.7]
// implementatation
// first create your sorter, the ordering array (used to create the SortBy instance) must contain Int, Float, Double, CGFloat or String
if let sorter = SortBy(arr: arr3) {
do {
arr1 = try sorter.sort(arr1)
arr2 = try sorter.sort(arr2)
arr3 = try sorter.sort(arr3)
}
catch SortError.ArrayLengthsNotEqual {
print("Array lengths are not equal")
}
catch {
print("other error")
}
}
@sketchytech
Copy link
Author

This is a way of sorting arrays as we find in spreadsheets for example, where an array represents a column of data or a row of data and that row or column is sorted in relation to several others. Incorporates stable sort code taken from AirspeedVelocity.

Example of sorting in place:

// data
var arr1 = [1,3,9,3,4,5,6,7,8]
var arr2 = ["A","C","B","X","C","D","E","F","G"]
var arr3 = [2.3,5.2,0.7,2.3,10.6,7.9,2.3,6.4,15.7]

// implementation
// first create your sorter,  the ordering array (used to create the SortBy instance) must contain Int, Float, Double, CGFloat or String
if let sorter = SortBy(arr: arr3) {
    do {
        try sorter.sortInPlace(&arr3)
        try sorter.sortInPlace(&arr2)
        try sorter.sortInPlace(&arr1)
    }
    catch SortError.ArrayLengthsNotEqual {
        print("Array lengths are not equal")
    }
    catch {
        print("other error")
    }
}

@sketchytech
Copy link
Author

To sortInPlace: do the following

// data
var arr1 = [1,3,9,3,4,5,6,7,8]
var arr2 = ["A","C","B","X","C","D","E","F","G"]
var arr3 = [2.3,5.2,0.7,2.3,10.6,7.9,2.3,6.4,15.7]

// implementation
// first create your sorter,  the ordering array (used to create the SortBy instance) must contain Int, Float, Double, CGFloat or String
if let sorter = SortBy(arr: arr3) {
    do {
        try sorter.sortInPlace(&arr3)
        try sorter.sortInPlace(&arr2)
        try sorter.sortInPlace(&arr1)
    }
    catch SortError.ArrayLengthsNotEqual {
        print("Array lengths are not equal")
    }
    catch {
        print("other error")
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment