Jay Jay - 5 months ago 11
Swift Question

How to sort an array of Structures with/by dynamic property

Given an

NSTableView
that has an array of structures as its datasource. A user can click on any column heading to sort by that column. The column identifiers match the property names of the properties within the structure.

Given a structure

struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
}


and an array of structures

var myArray = [MyStructure]()


The goal is that when a column heading is clicked, use that column's identifier to sort the array of structures by that column identifier/property

With an array of dictionaries, it was easy...

self.myArrayOfDictionaries.sortInPlace {
(dictOne, dictTwo) -> Bool in
let d1 = dictOne[colIdentifier]! as String;
let d2 = dictTwo[colIdentifier]! as String;

return d1 < d2 //or return d1 > d2 for reverse sort
}


The question is how to access the properties of the Structure dynamically, something like

let struct = myArray[10] as! MyStructure //get the 10th structure in the array
let value = struct["col0data"] as! String //get the value of the col0data property


If there is a better way, suggestions would be appreciated.

I should also note that the structure may have 50 properties so this is an effort to reduce the amount of code needed to sort the array by any one of those properties.

edit:

One solution is to change the structure to a class derived from NSObject. Then the properties could be accessed via .valueForKey("some key"). However, I am trying to keep this Swifty.

Answer

I would definitely recommend simply embedding your dictionary into your struct. A dictionary is a much more suitable data structure for 50 key-value pairs than 50 properties – and you've said that this would be an acceptable solution.

Embedding the dictionary in your struct will give you the best of both worlds – you can easily encapsulate logic & you have have easy lookup of the values for each column ID.

You can now simply sort your array of structures like this:

struct MyStructure {

    var dict = [String:String]()

    init(col0Data:String, col1Data:String) {
        dict["col0data"] = col0Data
        dict["col1data"] = col1Data
    }
}


var myArray = [MyStructure(col0Data: "foo", col1Data: "bar"), MyStructure(col0Data: "bar", col1Data: "foo")]

var column = "col0data"
myArray.sort {
    $0.dict[column] < $1.dict[column]
}

print(myArray) // [MyStructure(dict: ["col0data": "bar", "col1data": "foo"]), MyStructure(dict: ["col0data": "foo", "col1data": "bar"])]

column = "col1data"
myArray.sort {
    $0.dict[column] < $1.dict[column]
}

print(myArray) // MyStructure(dict: ["col0data": "foo", "col1data": "bar"])], [MyStructure(dict: ["col0data": "bar", "col1data": "foo"])