根据动态属性列表对Swift Array进行排序

时间:2015-10-08 18:07:08

标签: swift sorting swifty-json

我有一个动态的属性数组,例如:

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({$0[compareAttribute] < $1[compareAttribute]})

我需要的是这样的方法:

fun sortArray(responseObjects: [JSON], sortProperties: [string]) {
     //perform the sort
}

我怎样才能迅速实现这一目标?

由于

1 个答案:

答案 0 :(得分:1)

我不知道你想走多远。找到一个Swifty方法来做到这一点并不容易,因为Swift有严格的输入和没有内省。在Objective-C中实现自己的排序描述符方法的一切都很难在Swift中完成。 NSSortDescriptor依赖于键值编码,这正是JSON对象中缺少的原因,因为它是一个Swift结构。你需要的是一个NSObject衍生物。如果您使用内置的NSJSONSerialization,那就是您现在拥有的,其余的将很容易。但是当你使用SwiftJSON时,你就抛弃了这个功能。

然而,仅作为如何通过&#34;键&#34;进行分类的示例在特殊情况下,让我们想象一下这样的事情:

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方法来获取每个结构的String值。属性基于属性的字符串名称。

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")
]

如果你仔细想想,那就是正确的答案。首先,我们对&#34; p3&#34;进行排序,因此p3值为"a"的Thing排在第一位。但对于其他两个,他们的p3值是相同的,所以我们回到他们的p3值。它们也是一样的,所以我们会回到p1的{​​{1}}值,"y""z"之前。