Sorting a Swift array by ordering from another array

Here’s a generic Swift 5.2 solution.

First we need to define a protocol that holds a property that is used to reorder our elements:

protocol Reorderable {
    associatedtype OrderElement: Equatable
    var orderElement: OrderElement { get }
}

Then we need to extend Array with elements conforming to Reorderable and implement our reordering function:

extension Array where Element: Reorderable {

    func reorder(by preferredOrder: [Element.OrderElement]) -> [Element] {
        sorted {
            guard let first = preferredOrder.firstIndex(of: $0.orderElement) else {
                return false
            }

            guard let second = preferredOrder.firstIndex(of: $1.orderElement) else {
                return true
            }

            return first < second
        }
    }
}

Example

Let’s assume you have defined:

struct Player {
    let position: String
}

let currentPositions = ["RB", "AA", "BB", "CC", "WR", "TE"]
let players = currentPositions.map { Player(position: $0) }

Now, all you need to do is conform Player to Reorderable:

extension Player: Reorderable {
    typealias OrderElement = String
    var orderElement: OrderElement { position }
}

You can now use:

let sortedPlayers = players.reorder(by: ["QB", "WR", "RB", "TE"])

print(sortedPlayers.map { $0.position })
// ["WR", "RB", "TE", "AA", "BB", "CC"]

Original Solution

This is a generic Swift 5.2 solution based on OuSS’ code and requires array elements to be Equatable.

extension Array where Element: Equatable {

    func reorder(by preferredOrder: [Element]) -> [Element] {

        return self.sorted { (a, b) -> Bool in
            guard let first = preferredOrder.firstIndex(of: a) else {
                return false
            }

            guard let second = preferredOrder.firstIndex(of: b) else {
                return true
            }

            return first < second
        }
    }
}

let currentPositions = ["RB", "AA", "BB", "CC", "WR", "TE"]
let preferredOrder = ["QB", "WR", "RB", "TE"]
let sorted = currentPositions.reorder(by: preferredOrder)
print(sorted) // ["WR", "RB", "TE", "AA", "BB", "CC"]

Leave a Comment