Last active
March 7, 2016 16:10
-
-
Save sketchytech/5a8f264c4195d888692c to your computer and use it in GitHub Desktop.
Swift: A means of secondary sorting with error handling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | |
} | |
} |
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
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: