键路径值类型 'Int' 无法转换为上下文类型 'String'

Key path value type 'Int' cannot be converted to contextual type 'String'

我正在尝试将包含 KeyPath 和一种排序顺序类型的多个元组传递给应该进行排序的方法。

我有这个方法:

extension Array {
    mutating func sort<T: Comparable>(by criteria: (path: KeyPath<Element, T>, order:OrderType)...) {
        
        criteria.forEach { path, order in
            //...
            sort { first, second in
            order.makeComparator()(
                first[keyPath: path],
                second[keyPath: path]
            )
        }
        }
    }
}

我是这样使用它的:

var posts = BlogPost.examples
        
posts.sort(by:(path:\.pageViews, order: .asc), (path:\.sessionDuration, order: .desc))

现在,因为 pageViewssessionDuration 属性都是 integers,这将起作用。

但是如果我想传递两个不同类型的属性(比如 StringInt),我会得到这个错误:

Key path value type 'Int' cannot be converted to contextual type 'String'

这是代码的其余部分,但我想不是那么相关:

enum OrderType: String {
    case asc
    case desc
}

extension OrderType {
    func makeComparator<T: Comparable>() -> (T, T) -> Bool {
        switch self {
        case .asc:
            return (<)
        case .desc:
            return (>)
        }
    }
}

我应该如何定义排序方法以便它接受异构 键路径?

目前还没有可变参数泛型,因此您需要编写一个 AnyComparable 类型的橡皮擦。这个改编自这个 post .

struct AnyComparable: Equatable, Comparable {
    private let lessThan: (Any) -> Bool
    private let value: Any
    private let equals: (Any) -> Bool

    public static func == (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
        lhs.equals(rhs.value) || rhs.equals(lhs.value)
    }
    
    public init<C: Comparable>(_ value: C) {
        self.value = value
        self.equals = { [=10=] as? C == value }
        self.lessThan = { ([=10=] as? C).map { value < [=10=] } ?? false }
    }

    public static func < (lhs: AnyComparable, rhs: AnyComparable) -> Bool {
        lhs.lessThan(rhs.value) || (rhs != lhs && !rhs.lessThan(lhs.value))
    }
}

这样,您可以将 sort 方法签名写为:

mutating func sort(by criteria: (path: KeyPath<Element, AnyComparable>, order:OrderType)...) {
    
    
}

为了方便我们传递类型为AnyComparable的关键路径,我们可以做一个扩展:

extension Comparable {
    // this name might be too long, but I'm sure you can come up with a better name
    var anyComparable: AnyComparable {
        .init(self)
    }
}

现在我们可以做:

someArray.sort(by: (\.key1.anyComparable, .asc), (\.key2.anyComparable, .asc))