根据属性的动态列表对 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。所以你只需要使用 Swift sort 方法。你知道该怎么做,对吧? 我一直在查看 JSON 文档,它没有 nameis_activeage 属性。那么您究竟是如何期望它起作用的呢? 谢谢,这是 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 数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法根据小数位在python中对数组/列表的值进行动态分组?

使用动态 LINQ 按一个或多个属性对 JSON 进行排序

根据总值对对象数组列表进行排序

如何根据布尔属性对对象数组进行排序?

Swift如何按距离对表格视图进行排序

Swift 2.0 按属性对对象数组进行排序