根据属性的动态列表对 Swift 数组进行排序
Posted
技术标签:
【中文标题】根据属性的动态列表对 Swift 数组进行排序【英文标题】:Sorting Swift Array according to dynamic list of attributes 【发布时间】:2015-10-08 18:07:08 【问题描述】:我有一个动态的属性数组,例如:
var compareAttributes = ["name", "is_active", "age"]
项目及其数量始终可以更改。
我需要根据此列表中的所有属性(按顺序)对数组进行排序,所有这些属性都在 ACCEDING 中。
我尝试使用 NSSortDiscrptors
的 OBJ-C 方式,但由于我的 Array 包含 [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($0[compareAttribute] < $1[compareAttribute])
我需要的是这样的方法:
fun sortArray(responseObjects: [JSON], sortProperties: [string])
//perform the sort
我怎样才能快速做到这一点?
谢谢
【问题讨论】:
什么是 JSON 对象,为什么不能将数组转换为 NSArray? 这是 SwiftyJSON 对象,这就是编译器所抱怨的。我以为它只能将 swift 类型转换为 NSArray ? 是的,你是对的,对不起;他们已经把它做成了一个 Swift 结构,你不能把它们做成一个 NSArray。所以你只需要使用 Swiftsort
方法。你知道该怎么做,对吧?
我一直在查看 JSON 文档,它没有 name
或 is_active
或 age
属性。那么您究竟是如何期望它起作用的呢?
谢谢,这是 JSON 对象中的值。我知道如何使用排序,我用 1 个属性对这个 JSON 对象进行了排序。我需要找到一个方法来创建一个方法,该方法将获取属性数组并对 JSON 数组进行排序。我将在问题中添加我使用的 1 属性排序。
【参考方案1】:
我不确定你想在这方面走多远。找到一种 Swifty 的方式来做到这一点一点也不容易,因为 Swift 有严格的类型和没有内省。在 Objective-C 中实现你自己的 sort-by-sort-descriptor 方法的所有事情在 Swift 中都很难做到。 NSSortDescriptor 依赖于键值编码,这正是 JSON 对象中 missing 的内容,因为它是一个 Swift 结构。您在这里需要的是一个 NSObject 派生类。如果您一直使用内置的 NSJSONSerialization,这就是您现在所拥有的,剩下的就很容易了。但是,当您改用 SwiftJSON 时,您放弃了该功能。
但是,作为一个在特殊情况下如何通过“键”进行子排序的示例,让我们想象一下这样的事情:
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($0, $1, 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"
之前。
【讨论】:
感谢您的精彩回答。我在国外,刚刚看到它:)。我最终将所有对象转换为等效的 NSMangedObjects 并使用了 fetch 请求。不过谢谢。以上是关于根据属性的动态列表对 Swift 数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章