对于仿制药而言,这似乎是一项工作,而且我已经接近了,但是有一个漏洞.
这就是我现在所处的位置:
func compare<T:Comparable>(lft: T,_ rgt: T) -> Bool { return lft < rgt } func orderBy(sortField: String) -> [Thingie] { let allArray = (self.thingies as NSSet).allObjects as! [Thingie] //typealias T = the type of allArray[0][sortField] // or maybe create an alias that conforms to a protocol: //typealias T:Comparable = ? return allArray.sort({(a,b) -> Bool in return self.compare(a[sortField] as! T,b[sortField] as! T) }) }
我使用泛型创建了一个比较函数,并在我的排序例程中调用它.问题是AnyObject!不适用于我的泛型,所以我需要将[sortField]和b [sortField]返回的值转换为相同的类型.只要编译器很高兴两个值都属于同一类型并且它实现了Comparable协议,它甚至不重要.
我认为一个typealias可以做到这一点,但也许有更好的方法?
附带问题:肯定有一种更好的方法可以从集合中创建初始的,未排序的数组而不需要使用NSSet.欢迎一点点暗示. [解决了那一点!谢谢,Oliver Atkinson!]
这里有一大堆代码可以粘贴到游乐场.它在orderBy实现上有三次尝试,每次尝试都有问题.
//: Playground - noun: a place where people can play import Foundation class Thingie: Hashable { var data: [String: AnyObject] var hashValue: Int init(data: [String: AnyObject]) { self.data = data self.hashValue = (data["id"])!.hashValue } subscript(propName: String) -> AnyObject! { return self.data[propName] } } func ==(lhs: Thingie,rhs: Thingie) -> Bool { return lhs.hashValue == rhs.hashValue } var thingies: Set = Set<Thingie>() thingies.insert(Thingie(data: ["id": 2,"description": "two"])); thingies.insert(Thingie(data: ["id": 11,"description": "eleven"])); // attempt 1 // won't compile because '<' won't work when type is ambiguous e.g.,AnyObject func orderByField1(sortField: String) -> [Thingie] { return thingies.sort { $0[sortField] < $1[sortField] } } // compare function that promises the compiler that the operands for < will be of the same type: func compare<T:Comparable>(lft: T,_ rgt: T) -> Bool { return lft < rgt } // attempt 2 // This compiles but will bomb at runtime if Thingie[sortField] is not a string func orderByField2(sortField: String) -> [Thingie] { return thingies.sort { compare($0[sortField] as! String,$1[sortField] as! String) } } // attempt 3 // Something like this would be ideal,but protocol Comparable can't be used like this. // I SUSPECT the underlying reason that Comparable can't be used as a type is the same thing preventing me from making this work. func orderByField3(sortField: String) -> [Thingie] { return thingies.sort { compare($0[sortField] as! Comparable,$1[sortField] as! Comparable) } } // tests - can't run until a compiling candidate is written,of course // should return array with thingie id=2 first: var thingieList: Array = orderByField2("id"); print(thingieList[0]["id"]) // should return array with thingie id=11 first: var thingieList2: Array = orderByField2("description"); print(thingieList2[0]["id"])
以下方法解决了这些问题. (请原谅我没有心去删除我以前的答案;让我们说它的局限性是有益的……)
和以前一样,我们将从目标API开始:
struct Thing : ThingType { let properties: [String:Sortable] subscript(key: String) -> Sortable? { return properties[key] } } let data: [[String:Sortable]] = [ ["id": 1,"description": "one"],["id": 2,"description": "two"],["id": 3,"description": "three"],["id": 4,"description": "four"],"description": "four"] ] var things = data.map(Thing.init) things.sortInPlaceBy("id") things .map{ $0["id"]! } // [1,2,3,4] things.sortInPlaceBy("description") things .map{ $0["description"]! } // ["four","one","three","two"]
为了实现这一点,我们必须拥有这个ThingType协议和可变集合的扩展(这将适用于集合和数组):
protocol ThingType { subscript(_: String) -> Sortable? { get } } extension MutableCollectionType where Index : RandomAccessIndexType,Generator.Element : ThingType { mutating func sortInPlaceBy(key: String,ascending: Bool = true) { sortInPlace { guard let lhs = $0[key],let rhs = $1[key] else { return false // Todo: nil handling } guard let b = (try? lhs.isOrderedBefore(rhs,ascending: ascending)) else { return false // Todo: handle SortableError } return b } } }
显然,整个想法围绕这个Sortable协议:
protocol Sortable { func isOrderedBefore(_: Sortable,ascending: Bool) throws -> Bool }
……可以通过我们想要使用的任何类型独立地符合:
import Foundation extension NSNumber : Sortable { func isOrderedBefore(other: Sortable,ascending: Bool) throws -> Bool { try throwIfTypeNotEqualTo(other) let f: (Double,Double) -> Bool = ascending ? (<) : (>) return f(doubleValue,(other as! NSNumber).doubleValue) } } extension Nsstring : Sortable { func isOrderedBefore(other: Sortable,ascending: Bool) throws -> Bool { try throwIfTypeNotEqualTo(other) let f: (String,String) -> Bool = ascending ? (<) : (>) return f(self as String,other as! String) } } // Todo: make more types Sortable (including those that do not conform to NSObject or even AnyObject)!
这个throwIfTypeNotEqualTo方法只是Sortable的便利扩展:
enum SortableError : ErrorType { case TypesNotEqual } extension Sortable { func throwIfTypeNotEqualTo(other: Sortable) throws { guard other.dynamicType == self.dynamicType else { throw SortableError.TypesNotEqual } } }
就是这样.现在,即使在框架之外,我们也可以将新类型符合Sortable,并且类型检查器在编译时验证我们的[[String:Sortable]]源数据.此外,如果将Thing扩展为符合Hashable,则Set< Thing>也可按钥匙排序……
请注意,虽然Sortable本身不受约束(这很棒),但是如果需要,可以使用以下协议将源数据和Thing的属性约束为具有NSObject或AnyObject值的字典:
protocol SortableNSObjectType : Sortable,NSObjectProtocol { }
…或者更直接地通过将数据和Thing的属性声明为:
let _: [String : protocol<Sortable,NSObjectProtocol>]
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。