I am not a computer science person, but it seems to me that this business of repeatedly calling indexOf
is wasteful. Surely the right approach would be to walk through the template array (ids
in this case) once in advance, building a dictionary that associates each element with its position in the array:
let ids = ["1", "2", "3"]
var d = [String:Int]()
for (ix,id) in ids.enumerated() {
d[id] = ix
}
Now we've got a dictionary, and lookup in a dictionary is fast. So we can use each object's id
as the key and sort on the corresponding value. Let's say that this is our initial array of objects:
class MyObject {
let id: String
init(id:String) {self.id = id}
}
let objects = [MyObject(id:"3"), MyObject(id:"1"), MyObject(id:"2")]
Now sorting is a one-liner:
let objectsSorted = objects.sorted { d[$0.id]! < d[$1.id]! }
The advantage here is especially obvious if you know you're going to be using ids
as a template often, because you only have to form the dictionary d
once and now you're ready to sort against that template as many times as you like. In effect, the dictionary memoizes the sort order. (Granted, I have not taken into account what happens if the dictionary lookup were to fail; we just crash.)
We can generalize that approach, based on the fact that in order to serve as a dictionary key, the values in the template array must be hashable:
struct Cosorter<K:Hashable> {
let d : [K:Int]
init(_ arr:[K]) {
var dd = [K:Int]()
for (ix,val) in arr.enumerated() {
dd[val] = ix
}
self.d = dd
}
func lt(_ v1:K, _ v2:K) -> Bool {
return self.d[v1]! < self.d[v2]!
}
}
Now each template is transformed into a Cosorter instance initialized with the template array, thus causing the dictionary to be prepared, once:
let idsTemplate = Cosorter(ids)
Any time we want to cosort on that template, we just use that template's lt
as the ordering function:
let objectsSorted = objects.sorted {idsTemplate.lt($0.id,$1.id)}