Given an array defined as follow
let list: [Any]
I want to sort it WHEN
Element
Element
is Comparable
.So I would need a function that when the array is populated in a way like the followings
let list: [Any] = [10, 11, 0, 2, -1]
let list: [Any] = ["Red", "Green", "Blue"]
let list: [Any] = [true, false, true, true]
does return the sorted array.
On the other hand when list
contains one of the following examples
let list: [Any] = [CGPointZero, CGPoint(x:1, y:1)] // CGPoint is not comparable
let list: [Any] = [10, "Hello"] // Values of different types
I want nil
as return value.
Any idea?
Sorting an array means to arrange the elements in the array in a certain order. Various algorithms have been designed that sort the array using different methods. Some of these sorts are more useful than the others in certain situations.
Just like numeric arrays, you can also sort string array using the sort function. When you pass the string array, the array is sorted in ascending alphabetical order. To sort the array in descending alphabetical order, you should provide the Collections interface method reverseOrder () as the second argument.
To sort the subarray, the Arrays class provides the static method named sort (). It sorts the specified range of the array into ascending order. We can also sort the array of type long, double, float, char, byte, etc. a: An array to be sort.
When you pass the string array, the array is sorted in ascending alphabetical order. To sort the array in descending alphabetical order, you should provide the Collections interface method reverseOrder () as the second argument. The following program demonstrates the sorting of a string array in ascending as well as descending order.
Compile time solution
extension _ArrayType where Generator.Element == Any {
func sortQ() -> Any? {
return nil
}
}
extension _ArrayType where Generator.Element: Comparable {
func sortQ() -> [Self.Generator.Element] {
return self.sort(<)
}
}
// Because Bool is not comparable by default...
extension Bool: Comparable {
}
public func < (lhs: Bool, rhs: Bool) -> Bool {
return !lhs && rhs // or Int(lhs) < Int(rhs)
}
[10, 11, 0, 2, -1].sortQ() //[-1, 0, 2, 10, 11]
["Red", "Green", "Blue"].sortQ() //["Blue", "Green", "Red"]
[true, false, true, true].sortQ() //[false, true, true, true]
[CGPointZero, CGPoint(x:1, y:1)].sortQ() //nil
[10, "Hello"].sortQ() //nil
Runtime solutions:
UPDATE
Here is non final state. The problem is with casting to comparable. IMHO it is not possible. Until now I didn't know about trick with optional type. Anyway even casting of meta type is not possible because type is not known until runtime. My weak workaround is to list supported comparable types:
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
let f = Mirror(reflecting: firstElement)
let s = Mirror(reflecting: secondElement)
guard f.subjectType == s.subjectType else {
arrayOK = false
return false
}
switch String(f.subjectType) {
case "Int":
return (firstElement as! Int) < (secondElement as! Int)
case "String":
return (firstElement as! String) < (secondElement as! String)
case "Bool":
return (firstElement as! Bool) < (secondElement as! Bool)
default:
arrayOK = false
return false
}
}
return arrayOK ? sortedArray : nil
}
}
UPDATE 2
The second option is to have comparable protocol defined differently (AnyComparable
). Unfortunately it means to create extensions for all Comparable types.
Otherwise there's no way, at compile-time, the compiler can find the correct function/operator (as it doesn't know the types ahead of time).
So you have two options:
IMHO there is no other solution
protocol AnyComparable {
func compareTo(second: Any) -> Bool
}
extension AnyComparable where Self: Comparable {
func compareTo(second: Any) -> Bool {
if let secondSameType = second as? Self {
return self < secondSameType
}
return false
}
}
extension Int: AnyComparable {
}
extension String: AnyComparable {
}
extension Bool: AnyComparable {
}
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
var wantedType: Any.Type?
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
if wantedType == nil {
wantedType = Mirror(reflecting: firstElement).subjectType
}
guard let f = firstElement as? AnyComparable where wantedType == Mirror(reflecting: secondElement).subjectType else {
arrayOK = false
return false
}
return f.compareTo(secondElement)
}
return arrayOK ? sortedArray : nil
}
}
For the moment, I wrote a little extension to check if all the elements are of the same type (I will be working on this to check if can get a result):
extension _ArrayType where Generator.Element == Any{
func hasEqualTypeAndComparable()->Bool{
if self.count > 0{
let firstType = self.first?.dynamicType
for val in self{
if firstType != val.dynamicType{
return false
}
}
return self.first is Comparable
}
return false
}
}
Example:
//Example 1
var values:[Any] = [2,1,4,3,"Hola"]
values.hasEqualTypeAndComparable() // Print false
//Example 2
var values:[Any] = [2,1,4,3]
values.hasEqualTypeAndComparable() // Prints true
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