0

I have a dynamic Array of properties for example :

var compareAttributes = ["name", "is_active", "age"]

The items and their count can always be changed.

I need to sort an array according to all the properties in this list (By order) all of them ACCEDING.

I tried the OBJ-C way using NSSortDiscrptors But since my Array contains [JSON] objects I can not convert it to 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
    }

I know how to sort this with 1 property :

 responseObjects.sortInPlace({$0[compareAttribute] < $1[compareAttribute]})

what I need is a method like this :

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

How can I achieve that in a swift way ?

Thanks

8
  • What is a JSON object and why can't you convert the array to NSArray? Commented Oct 8, 2015 at 18:10
  • this is SwiftyJSON object, and that is what the compiler complains. I thought that it can only convert swift types to NSArray ? Commented Oct 8, 2015 at 18:12
  • Yes, you're right, sorry; they've made this a Swift struct, and you can't make an NSArray of those. So you will just have to use the Swift sort method. You know how to do that, right? Commented Oct 8, 2015 at 18:47
  • I've been looking at the JSON documentation and it doesn't have a name or an is_active or an age property. So how exactly were you expecting this to work? Commented Oct 8, 2015 at 18:51
  • Thanks, This are values in the JSON object. I know how to use sort and I sorted this JSON object with 1 property. I need to find away to create a method that will take the properties array and sort the JSON array. I will add the 1 property sort I used to the question. Commented Oct 8, 2015 at 19:05

1 Answer 1

1

I'm not sure how far you want to go with this. Finding a Swifty way to do this is not at all easy, because Swift has strict typing and no introspection. Everything about implementing your own sort-by-sort-descriptor method in Objective-C is hard to do in Swift. NSSortDescriptor depends upon key-value coding, which is exactly what is missing from a JSON object because it is a Swift struct. What you need here is an NSObject derivative. If you had stayed with the built-in NSJSONSerialization, that is what you would now have, and the rest would be easy. But you threw that feature away when you went with SwiftJSON instead.

However, just as an example of how to do subsorting by "key" in a special case, let's imagine something like this:

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 ""
        }
    }
}

You see what I've constructed here? It's a pure Swift struct, Thing, that has properties which I've made manually key-value-codable, as it were, by supplying a subscript method that fetches the String value of each of its properties based on the string name of the property.

The StringSubscriptable protocol is then merely a protocol-based way of guaranteeing that Thing has that ability.

Given all that, we can extend Array to do what you're asking for:

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) }
    }
}

The idea is that you hand sortByList a list of keys and we sort an array of Thing (because it is a StringSubscriptable) based on those keys in order. For each pair, if the first key is enough to determine the outcome, we sort on it, but if the first key gives equal results for this pair, we use the second key instead (that is what subsorting is about).

To illustrate:

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

The outcome is

[
 Thing(p1: "z", p2: "ho", p3: "a"), 
 Thing(p1: "y", p2: "ho", p3: "x"), 
 Thing(p1: "z", p2: "ho", p3: "x")
]

And if you think about it, that's the right answer. First we sort on "p3", so the Thing with p3 value "a" comes first. But for the other two, their p3 values are the same, so we fall back on their p3 values. They are the same as well, so we fall back on their p1 values, and "y" comes before "z".

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for a great answer. I was abroad and just now saw it :). I finally converted all the objects to their NSMangedObjects equivalent and used fetch request. But thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.