【问题标题】:Sorting the [Any] array对 [Any] 数组进行排序
【发布时间】:2016-11-04 07:50:04
【问题描述】:

给定一个定义如下的数组

let list: [Any]

我想对其进行排序何时

  1. 其中的所有值都具有相同的类型Element
  2. AND ElementComparable

什么时候应该返回排序后的数组

所以我需要一个函数,当数组以如下方式填充时

let list: [Any] = [10, 11, 0, 2, -1]
let list: [Any] = ["Red", "Green", "Blue"]
let list: [Any] = [true, false, true, true]

确实返回排序后的数组。

什么时候应该返回 nil

另一方面,当list 包含以下示例之一时

let list: [Any] = [CGPointZero, CGPoint(x:1, y:1)] // CGPoint is not comparable
let list: [Any] = [10, "Hello"] // Values of different types

我希望nil 作为返回值。

有什么想法吗?

【问题讨论】:

  • @shim:我没有找到任何有效的方法
  • @appzYourLife 那么,Element 类型是什么?
  • @pacification:真的是任何类型。它只需要Comparable。我发布的示例应该可以阐明这个概念。
  • 这里的棘手部分是 Swift 无法知道 Any “包装”的值是否具有可比性(参见 e.g. this gist),除非首先尝试将类型转换为具体类型 然后——如果比较成功——检查该类型是否为Comparable。这意味着你可以解决这个排序如果你同时提供了Any“包装”的类型,但我认为这消除了上述通用排序的全部目的。
  • 我相信这是 Swift 著名的静态类型/类型安全的一个给定限制(“缺点”)。可能您可以使用 Obj-C 技术和运行时内省在某种程度上规避这一点,但如果可能的话,我怀疑它会限制您使用 NSObject 子类包装器检查引用类型的 Comparable-ity(通过 Obj-C KVO)上课List,但只要再次阅读这句话,就会发现它的混乱...... :)

标签: arrays swift sorting generics introspection


【解决方案1】:

编译时解决方案

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

运行时解决方案:

更新

这是非最终状态。问题在于铸造可比。恕我直言,这是不可能的。直到现在我还不知道可选类型的技巧。无论如何,甚至无法转换元类型,因为直到运行时才知道类型。我较弱的解决方法是列出支持的可比较类型:

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
    }
}

更新 2

第二个选项是以不同方式定义可比较的协议 (AnyComparable)。不幸的是,这意味着为所有 Comparable 类型创建扩展。 否则,编译器无法在编译时找到正确的函数/运算符(因为它不提前知道类型)。

所以你有两个选择:

  1. 如果您对要比较和定义的类型有所了解 他们明确(更新1)。
  2. 使用不使用 Self 的接口 类型(更新 2)。

恕我直言,没有其他解决方案

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
    }
}

【讨论】:

  • Aaahhh... 现在我知道发生了什么事。您的意思是运行时检查,而不是编译器检查。请参阅我的更新答案。
  • @JMI:更新无法编译。无论如何,您的扩展程序不支持其他 Comparable(s) 类型。它应该适用于任何类型。
  • @appzYourLife 您使用的是 swift 2.3 还是 3?你遇到了什么错误?
  • @JMI:对不起,我错过了使 Bool Comparable 的扩展。但看起来这段代码只适用于 String、Bool 和 Int 的数组。如我的问题所述,我需要为每个Comparable(s) 数组提供解决方案。但是,感谢您尝试找到解决方案。我很感激。
  • @appzYourLife 查看我的更新 2。在运行时这是不可能的...您将不得不以某种方式帮助编译...
【解决方案2】:

如果您的用例允许您向编译器提供提示,您可以根据所需的输出类型指定过滤器:

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
    }
}

如果输入数组不包含任何指定类型的元素,那么它将只返回一个空数组。如果类型不是 Comparable,则代码不会事件编译。

例子:

let list: [Any] = [10, "Hello", 3, false, "Foo", "Bar", 1] // Values of different types

var output = list.filterByType() as [Int]
output.sortInPlace()

【讨论】:

  • 感谢您的回答。不幸的是,数组中的元素类型是未知的。它可以是任何类型。
【解决方案3】:

目前,我编写了一个小扩展来检查所有元素是否属于同一类型(我将对此进行工作以检查是否可以获得结果):

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

【讨论】:

  • 我改了return语句,我测试了,现在返回期望值。
  • 嗯...当被比较的类型是非可选的(即self.first! is Comparable)时,编译器会报错Comparable不能被使用,因为它有自我要求。我想知道为什么它适用于可选项?
  • @originaluser2 由于Comparable 是异构的,我推测包装在可选(.Some&lt;T&gt;.None)中的值被视为泛型,在这种情况下允许键入检查is Comparable,就像使用Comparable 作为类型约束(..&lt;T: Comparable&gt;),其中具体的展开值自然不是通用的,因此会产生编译时错误。
  • @dfri 嗯...虽然在这种情况下is Comparable 确实应该比较Optional&lt;T&gt; 本身 是否是Comparable(它不是直接的) -而不是简单地如果TComparable。尽管话虽如此,但这似乎正是它正在做的事情,看起来像是Optional 的一个特例,因为我无法用其他泛型类型重现它。很有趣。
  • @originaluser2 是的,确实很奇怪。我在this gist 中总结了这些要点,如果我无法在某处找到一些解释,不妨发布一个关于此的问题。但是现在,睡吧!
猜你喜欢
  • 2019-05-12
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
相关资源
最近更新 更多