Sorting the [Any] array

后端 未结 3 540
栀梦
栀梦 2020-12-28 08:41

Given an array defined as follow

let list: [Any]

I want to sort it WHEN

  1. all the values inside it have the sa
相关标签:
3条回答
  • 2020-12-28 09:14

    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
    
    0 讨论(0)
  • 2020-12-28 09:19

    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:

    1. if you had some idea of the types you were comparing and define them explicitly (update 1).
    2. Use interface which does not use Self type (update 2).

    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
        }
    }
    
    0 讨论(0)
  • 2020-12-28 09:22

    If your use case allows you to provide a hint to the compiler, you could specify a filter on the type of output that you want:

    extension _ArrayType where Generator.Element == Any {
    
        func filterByType<T: Comparable>() -> [T] {
    
            var output = [T]()
    
            for i in self {
                if let j = i as? T {
                    output.append(j)
                }
            }
    
            return output
        }
    }
    

    If the input array does not contain any elements of the specified type then it will just return an empty array. If the type is not a Comparable, then the code won't event compile.

    Example:

    let list: [Any] = [10, "Hello", 3, false, "Foo", "Bar", 1] // Values of different types
    
    var output = list.filterByType() as [Int]
    output.sortInPlace()
    
    0 讨论(0)
提交回复
热议问题