根据动态属性列表对 Swift 数组进行排序
Sorting Swift Array according to dynamic list of attributes
我有一个动态属性数组,例如:
var compareAttributes = ["name", "is_active", "age"]
项目及其数量始终可以更改。
我需要根据此列表中的所有属性(按顺序)对数组进行排序,所有属性都是 ACCEDING。
我尝试使用 NSSortDiscrptors
的 OBJ-C 方式,但由于我的数组包含 [JSON]
个对象,我无法将其转换为 NSArray :
class func sortServerObjectsByCompareAttributes( responseObjects: [JSON]) {
var sortDiscriptors = [NSSortDescriptor]()
for attribute in compareAttributes {
sortDiscriptors.append(NSSortDescriptor(key: attribute, ascending: true))
}
let sortedByAge = (responseObjects as NSArray).sortedArrayUsingDescriptors(sortDiscriptors) // error is [JSON] is not convertible to NSArray
}
我知道如何用 1 属性 排序:
responseObjects.sortInPlace({[=13=][compareAttribute] < [compareAttribute]})
我需要的是这样的方法:
fun sortArray(responseObjects: [JSON], sortProperties: [string]) {
//perform the sort
}
我怎样才能以 swift 的方式实现它?
谢谢
我不确定你想在这方面走多远。找到一个 Swifty 方法来做到这一点并不容易,因为 Swift 具有严格的类型并且没有内省。在 Objective-C 中实现您自己的按排序描述符排序方法的所有事情都很难在 Swift 中完成。 NSSortDescriptor 依赖于键值编码,这正是 JSON 对象中 缺失的 因为它是一个 Swift 结构。您在这里需要的是一个 NSObject 衍生物。如果您一直使用内置的 NSJSONSerialization,那么这就是您现在所拥有的,剩下的就很容易了。但是,当您改用 SwiftJSON 时,您就放弃了该功能。
但是,作为在特殊情况下如何通过 "key" 进行子排序的示例,让我们想象这样的事情:
protocol StringSubscriptable {
subscript(which:String) -> String {get}
}
struct Thing : StringSubscriptable {
var p1 : String
var p2 : String
var p3 : String
subscript(which:String) -> String {
switch which {
case "p1": return p1
case "p2": return p2
case "p3": return p3
default: return ""
}
}
}
你看到我在这里构造了什么了吗?这是一个纯 Swift 结构,Thing,它具有我手动设置为键值编码的属性,实际上是通过提供一个 subscript
方法来获取其每个属性的字符串值基于 属性.
的字符串名称
StringSubscriptable 协议只是一种基于协议的方式来保证 Thing 具有这种能力。
考虑到所有这些,我们可以扩展 Array 来满足您的要求:
extension Array where Element : StringSubscriptable {
mutating func sortByList(list:[String]) {
func sortHelper(a:StringSubscriptable, _ b:StringSubscriptable, _ ix:Int) -> Bool {
let key = list[ix]
if a[key] == b[key] && ix < list.count - 1 {
return sortHelper(a, b, ix + 1)
}
return a[key] < b[key]
}
self.sortInPlace { sortHelper([=11=], , 0) }
}
}
我们的想法是,您将 sortByList
一个键列表交给我们,然后我们根据这些键按顺序对 Thing 数组(因为它是一个 StringSubscriptable)进行排序。对于每一对,如果第一个键足以确定结果,我们将对其进行排序,但如果第一个键为这对键给出相同的结果,我们将使用第二个键代替(这就是子排序的意义所在)。
举例说明:
let t1 = Thing(p1: "y", p2: "ho", p3: "x")
let t2 = Thing(p1: "z", p2: "ho", p3: "x")
let t3 = Thing(p1: "z", p2: "ho", p3: "a")
var arr = [t1,t2,t3]
arr.sortByList(["p3", "p2", "p1"])
结果是
[
Thing(p1: "z", p2: "ho", p3: "a"),
Thing(p1: "y", p2: "ho", p3: "x"),
Thing(p1: "z", p2: "ho", p3: "x")
]
如果您仔细想想,这就是正确答案。首先,我们对 "p3" 进行排序,因此具有 p3
值 "a"
的事物排在第一位。但对于另外两个,它们的 p3
值是相同的,所以我们求助于它们的 p3
值。它们也相同,所以我们求助于它们的 p1
值,并且 "y"
出现在 "z"
.
之前
我有一个动态属性数组,例如:
var compareAttributes = ["name", "is_active", "age"]
项目及其数量始终可以更改。
我需要根据此列表中的所有属性(按顺序)对数组进行排序,所有属性都是 ACCEDING。
我尝试使用 NSSortDiscrptors
的 OBJ-C 方式,但由于我的数组包含 [JSON]
个对象,我无法将其转换为 NSArray :
class func sortServerObjectsByCompareAttributes( responseObjects: [JSON]) {
var sortDiscriptors = [NSSortDescriptor]()
for attribute in compareAttributes {
sortDiscriptors.append(NSSortDescriptor(key: attribute, ascending: true))
}
let sortedByAge = (responseObjects as NSArray).sortedArrayUsingDescriptors(sortDiscriptors) // error is [JSON] is not convertible to NSArray
}
我知道如何用 1 属性 排序:
responseObjects.sortInPlace({[=13=][compareAttribute] < [compareAttribute]})
我需要的是这样的方法:
fun sortArray(responseObjects: [JSON], sortProperties: [string]) {
//perform the sort
}
我怎样才能以 swift 的方式实现它?
谢谢
我不确定你想在这方面走多远。找到一个 Swifty 方法来做到这一点并不容易,因为 Swift 具有严格的类型并且没有内省。在 Objective-C 中实现您自己的按排序描述符排序方法的所有事情都很难在 Swift 中完成。 NSSortDescriptor 依赖于键值编码,这正是 JSON 对象中 缺失的 因为它是一个 Swift 结构。您在这里需要的是一个 NSObject 衍生物。如果您一直使用内置的 NSJSONSerialization,那么这就是您现在所拥有的,剩下的就很容易了。但是,当您改用 SwiftJSON 时,您就放弃了该功能。
但是,作为在特殊情况下如何通过 "key" 进行子排序的示例,让我们想象这样的事情:
protocol StringSubscriptable {
subscript(which:String) -> String {get}
}
struct Thing : StringSubscriptable {
var p1 : String
var p2 : String
var p3 : String
subscript(which:String) -> String {
switch which {
case "p1": return p1
case "p2": return p2
case "p3": return p3
default: return ""
}
}
}
你看到我在这里构造了什么了吗?这是一个纯 Swift 结构,Thing,它具有我手动设置为键值编码的属性,实际上是通过提供一个 subscript
方法来获取其每个属性的字符串值基于 属性.
StringSubscriptable 协议只是一种基于协议的方式来保证 Thing 具有这种能力。
考虑到所有这些,我们可以扩展 Array 来满足您的要求:
extension Array where Element : StringSubscriptable {
mutating func sortByList(list:[String]) {
func sortHelper(a:StringSubscriptable, _ b:StringSubscriptable, _ ix:Int) -> Bool {
let key = list[ix]
if a[key] == b[key] && ix < list.count - 1 {
return sortHelper(a, b, ix + 1)
}
return a[key] < b[key]
}
self.sortInPlace { sortHelper([=11=], , 0) }
}
}
我们的想法是,您将 sortByList
一个键列表交给我们,然后我们根据这些键按顺序对 Thing 数组(因为它是一个 StringSubscriptable)进行排序。对于每一对,如果第一个键足以确定结果,我们将对其进行排序,但如果第一个键为这对键给出相同的结果,我们将使用第二个键代替(这就是子排序的意义所在)。
举例说明:
let t1 = Thing(p1: "y", p2: "ho", p3: "x")
let t2 = Thing(p1: "z", p2: "ho", p3: "x")
let t3 = Thing(p1: "z", p2: "ho", p3: "a")
var arr = [t1,t2,t3]
arr.sortByList(["p3", "p2", "p1"])
结果是
[
Thing(p1: "z", p2: "ho", p3: "a"),
Thing(p1: "y", p2: "ho", p3: "x"),
Thing(p1: "z", p2: "ho", p3: "x")
]
如果您仔细想想,这就是正确答案。首先,我们对 "p3" 进行排序,因此具有 p3
值 "a"
的事物排在第一位。但对于另外两个,它们的 p3
值是相同的,所以我们求助于它们的 p3
值。它们也相同,所以我们求助于它们的 p1
值,并且 "y"
出现在 "z"
.