给定NSTableView
具有结构数组作为其数据源。用户可以单击任何列标题以按该列排序。列标识符与结构中属性的属性名称匹配。
给定结构
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
}
和一组结构
var myArray = [MyStructure]()
目标是在单击列标题时,使用该列的标识符按该列标识符/属性对结构数组进行排序
有一系列词典,很容易......
self.myArrayOfDictionaries.sortInPlace {
(dictOne, dictTwo) -> Bool in
let d1 = dictOne[colIdentifier]! as String;
let d2 = dictTwo[colIdentifier]! as String;
return d1 < d2 //or return d1 > d2 for reverse sort
}
问题是如何动态访问Structure的属性,如
let struct = myArray[10] as! MyStructure //get the 10th structure in the array
let value = struct["col0data"] as! String //get the value of the col0data property
如果有更好的方法,建议将不胜感激。
我还应该注意,该结构可能有50个属性,因此这是为了减少任何一个属性对数组进行排序所需的代码量。
编辑:
一种解决方案是将结构更改为从NSObject派生的类。然后可以通过.valueForKey(“some key”)访问属性。但是,我试图保持这个Swifty。
答案 0 :(得分:2)
也许我有解决问题的方法。此代码优于您的解决方案的优点是,您无需在结构中添加下标方法,以通过代码创建硬编码的String-Property-Value映射。
这是我的扩展
extension _ArrayType {
func sortedBy(propertyName propertyName: String) -> [Self.Generator.Element] {
let mirrors = self.map { Mirror(reflecting: $0) }
let propertyValues = mirrors.map { $0.children.filter { $0.label == propertyName }.first?.value }
let castedValues = propertyValues.map { $0 as? String }
let sortedArray = zip(self, castedValues).sort { (left, right) -> Bool in
return left.1 < right.1
}.map { $0.0 }
return sortedArray
}
}
struct Animal {
var name: String
var type: String
}
let animals = [
Animal(name: "Jerry", type: "Mouse"),
Animal(name: "Tom", type: "Cat"),
Animal(name: "Sylvester", type: "Cat")
]
animals.sortedBy(propertyName: "name")
// [{name "Jerry", type "Mouse"}, {name "Sylvester", type "Cat"}, {name "Tom", type "Cat"}]
animals.sortedBy(propertyName: "type")
// [{name "Tom", type "Cat"}, {name "Sylvester", type "Cat"}, {name "Jerry", type "Mouse"}]
此解决方案的最大限制是它仅适用于String属性。它必须在编译时才能更改为适用于任何类型的属性。现在我还没有一个解决方案,可以让它适用于任何属性类型的王而不改变代码。
我已经就问题的核心here寻求帮助。
答案 1 :(得分:1)
这是一个答案(但不是最好的答案);使用下标返回正确的属性,并在array.sort中设置要排序的属性:
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
subscript(key: String) -> String? { //the key will be the col identifier
get {
if key == "col0data" {
return col0data
} else if key == "col1data" {
return col1data
}
return nil
}
}
}
然后这是排序的工作方式:
let identifier = the column identifier string,say col0data in this case
myArray.sortInPlace ({
let my0 = $0[identifier]! //the identifier from the table col header
let my1 = $1[identifier]!
return my0 < my1
})
答案 2 :(得分:1)
我肯定会建议您将字典嵌入到结构中。字典是50个键值对比50个属性更合适的数据结构 - 你已经说过这将是一个可接受的解决方案。
在您的结构中嵌入字典将为您提供两全其美的优势 - 您可以轻松地封装逻辑和放大器。您可以轻松查找每个列ID的值。
您现在可以简单地对结构数组进行排序:
struct MyStructure {
var dict = [String:String]()
init(col0Data:String, col1Data:String) {
dict["col0data"] = col0Data
dict["col1data"] = col1Data
}
}
var myArray = [MyStructure(col0Data: "foo", col1Data: "bar"), MyStructure(col0Data: "bar", col1Data: "foo")]
var column = "col0data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // [MyStructure(dict: ["col0data": "bar", "col1data": "foo"]), MyStructure(dict: ["col0data": "foo", "col1data": "bar"])]
column = "col1data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // MyStructure(dict: ["col0data": "foo", "col1data": "bar"])], [MyStructure(dict: ["col0data": "bar", "col1data": "foo"])
答案 3 :(得分:-1)
如果你不知道MyStructure的值是什么类型,你将很难比较它们以对它们进行排序。如果你有一个可以比较你在MyStructure中可以拥有的所有类型的函数,那么这样的东西应该可以工作
struct OtherTypeNotComparable {
}
struct MyStructure {
var col0data = "cat" //name matches the column identifier
var col1data: OtherTypeNotComparable
}
let structures = [MyStructure(), MyStructure()]
let sortBy = "col1data"
func yourCompare(a: Any, b: Any) -> Bool {
return true
}
var expanded : [[(String, Any, MyStructure)]]
= structures.map { s in Mirror(reflecting: s).children.map { ($0!, $1, s) } }
expanded.sortInPlace { (a, b) -> Bool in
let aMatch = a.filter { $0.0 == sortBy }.first!.1
let bMatch = b.filter { $0.0 == sortBy }.first!.1
return yourCompare(aMatch, b: bMatch)
}