Swift 如何使用/按动态属性对结构数组进行排序
给定一个Swift 如何使用/按动态属性对结构数组进行排序,swift,macos,Swift,Macos,给定一个NSTableView,它的数据源是一个结构数组。用户可以单击任何列标题以按该列排序。列标识符与结构中属性的属性名称匹配 给定结构 struct MyStructure { var col0data = "" //name matches the column identifier var col1data = "" } 和一系列结构 var myArray = [MyStructure]() 目标是在单击列标题时,使用该列的标识符按该列标识符/属
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
}
如果有更好的方法,我们将不胜感激
我还应该注意,该结构可能有50个属性,因此这是为了减少按其中任何一个属性对数组进行排序所需的代码量
编辑:
一种解决方案是将结构更改为从NSObject派生的类。然后可以通过.valueForKey(“某个键”)访问属性。但是,我正在努力保持这种快速性。如果您不知道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)
}
来源:这里有一个答案(但不是最好的答案);使用下标返回正确的属性,并设置在数组中按哪个属性排序。排序:
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
})
let identifier=列标识符字符串,在本例中为col0data
myArray.sortInPlace({
让my0=$0[标识符]!//表列标题中的标识符
让my1=$1[标识符]!
返回my0
也许我有办法解决你的问题。与您的解决方案相比,此代码的优点在于,您不需要向结构中添加下标方法来通过代码创建硬编码字符串属性值映射
这是我的分机
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
}
}
局限性
此解决方案的最大限制是它仅适用于字符串属性。它可以更改为在编译时使用任何类型的属性。目前,我还没有一个解决方案,可以让它在不更改代码的情况下与任何属性类型的king一起工作
我已经就问题的核心寻求了帮助 我绝对建议只需将字典嵌入到结构中即可。对于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"])
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[列]<$1.dict[列]
}
打印(myArray)/[MyStructure(dict:[“col0data”:“bar”,“col1data”:“foo”]),MyStructure(dict:[“col0data”:“foo”,“col1data”:“bar”])]
column=“col1data”
myArray.sort{
$0.dict[列]<$1.dict[列]
}
print(myArray)//MyStructure(dict:[“col0data”:“foo”,“col1data”:“bar”)],[MyStructure(dict:[“col0data”:“bar”,“col1data”:“foo”])
为什么要在这里使用结构?有50个属性,数组或字典似乎是更合适的选择。如果需要封装额外的逻辑,那么可以将数组或字典作为结构的属性。@originaluser2我从一个字典数组开始,它在排序等方面发挥了出色的作用。然而,作为随着项目的发展,我需要添加对象逻辑,从而移动到一个结构或类。无论采用哪种方式,它们仍然需要根据从tableView标题单击中派生的属性之一进行排序。我可以使用一组if..then语句(if headerClick=“col0”然后按x排序,if headerClick=“col1”按y排序等)强制排序但这似乎很不雅观。那么为什么不简单地将字典作为结构的一个属性呢?这样你就可以得到值的封装和轻松查找。@originaluser2这是一个结构数组,用作tableView的数据源。因此它们(结构)需要按其中一个属性进行排序(以与单击的列标题匹配的为准)。如果字典嵌入到结构中,就没有办法对结构数组进行排序。但是,如果有办法,这是一个很好的解决方案,请将其作为答案发布!使用NSArrayController
和Cocoa绑定,那么您可以免费获得所有内容。但这需要一个类而不是一个结构作为模型。您假设属性的类型为String
。但在编译时您不知道。此答案的目的是对结构的属性进行排序吗?如果是,这不是最初的问题。问题是如何根据运行时确定的特定属性对数组中的结构进行排序。即,用户单击表列n heading,则我们知道按哪个属性对数组进行排序。属性名称将是从列标识符派生的字符串。我希望您通过…包含镜像函数。问题是在运行时比较未知类型,这些类型可能具有可比性,也可能不具有可比性。我更新了答案,以使用您可以编写该比较的函数如果要使用这种方法,有两个“任意”–您可以通过在键上使用开关语句来改进它,然后只需将不同的列标识符列为大小写即可。@originaluser2是的!好主意。@Jay:但是使用这种解决方案,您只需将硬编码部分从闭包移到下标方法…@appzYourLife I a格力·莫
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"}]
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"])