Another alternative: emanate from the orderArray to construct a sorted array
Another alternative (the "other way around" as compared to @MartinR:s answer) is to emanate from orderArray and simply construct the "sorted" array based on the number of occurences of elements in the orderArray in the array to be sorted.
Sub-alternative #1: allow an orderArray which is not comprehensive w.r.t. the elements to be sorted
In the example implementation of this alternative below, elements in the array to be sorted that are not present in the orderArray have been left in same relative order as in the original array, but at the end of the part of the array that does get sorted (i.e., after any elements that are included in orderArray). Another alternative could be nil return in case the orderArray does not fully cover all elements in the array to be sorted.
extension Array where Element: Equatable {
/* help method */
private func countInstances(of element: Element) -> Int {
return reduce(0) { $0 + ($1 == element ? 1 : 0) }
}
/* sort by ordered array method */
func sortBy(orderArray: [Element]) -> [Element] {
// construct sorted array based on elements in orderArray
return orderArray
.reduce([]) { $0 + Array(repeating: $1, count: countInstances(of: $1)) } +
filter { !orderArray.contains($0) }
}
}
Example usage:
let arr1 = ["second", "first", "unsortable", "second",
"anotherUnsortable", "fourth", "third", "second"]
let arr2 = ["foo", "first", "bar"]
let orderArray = ["first", "second", "third", "fourth"]
print(arr1.sortBy(orderArray: orderArray))
// ["first", "second", "second", "second", "third", "fourth", "unsortable", "anotherUnsortable"]
print(arr2.sortBy(orderArray: orderArray))
// ["first", "foo", "bar"]
Sub-alternative #2: for a non-comprehensive orderArray, return nil
In case you'd rather return nil if orderArray is not comprehensive w.r.t. the members of the array to be sorted, yuou can modify the sortBy method above accordingly:
extension Array where Element: Equatable {
private func countInstances(of element: Element) -> Int {
return reduce(0) { $0 + ($1 == element ? 1 : 0) }
}
func sortBy(orderArray: [Element]) -> [Element]? {
guard reduce(true, { $0 && orderArray.contains($1) }) else { return nil }
return orderArray
.reduce([]) { $0 + Array(repeating: $1, count: countInstances(of: $1)) }
}
}
Example usage:
let arr1 = ["second", "first", "second",
"fourth", "third", "second"]
let arr2 = ["foo", "baz", "bar"]
let orderArray = ["first", "second", "third", "fourth"]
print(arr1.sortBy(orderArray: orderArray))
// Optional(["first", "second", "second", "second", "third", "fourth"])
print(arr2.sortBy(orderArray: orderArray))
// nil