Search code examples
swiftgenericsextension-methods

Creating a Generic Sort function in Extension


I would like to know what I am doing wrong here. I would like not to use force casting but no matter what I try I do not seem able to.

extension Array where Element : Equatable {

    func highestPriority<T: Equatable>( priority: [T]) -> T? {
        let sorted = sorted { lhs, rhs in
            guard let lhs = priority.firstIndex(of: lhs as! T),
                  let rhs = priority.firstIndex(of: rhs as! T) else {
                return false
            }
            return lhs < rhs
        }
        return sorted.first as? T
    }
}

public enum Jobs: String, Codable {
    case wash = "Wash"
    case dry = "Dry"
    case wax = "Wax"
}

let priorities: [Jobs] = [.wash, .dry, .wax]

let jobs: [Jobs] = [.wax, .wash, .dry]

let priority = jobs.highestPriority(priority: priorities)

Im refining from this which works, but I'm not understanding why changing collection to use self in Array is any different?

func highestPriority<T:Equatable>(_ collection: [T]?, priority: [T]) -> T? {
    let sorted = collection?.sorted { lhs, rhs in
        guard let lhs = priority.firstIndex(of: lhs),
              let rhs = priority.firstIndex(of: rhs) else {
            return false
        }
        return lhs < rhs
    }
    return sorted?.first
}

public enum Jobs: String, Codable {
    case wash = "Wash"
    case dry = "Dry"
    case wax = "Wax"
}

let priorities: [Jobs] = [.wash, .dry, .wax]

let jobs: [Jobs] = [.wax, .wash, .dry]

let priority = highestPriority(jobs, priority: priorities)

I have tried changing the generic constraint but I'm just stabbing in the dark :D


Solution

  • Since all of the types associated with highestPriority are the same as the type stored in the array, change highestPriority from being generic to being based on Element.

    extension Array where Element : Equatable {
        func highestPriority(priority: [Element]) -> Element? {
            let sort = sorted { lhs, rhs in
                guard let lhs = priority.firstIndex(of: lhs),
                      let rhs = priority.firstIndex(of: rhs) else {
                    return false
                }
                return lhs < rhs
            }
    
            return sort.first
        }
    }
    

    Here's a much simpler implementation:

    extension Array where Element : Equatable {
        func highestPriority( priority: [Element]) -> Element? {
            return priority.first { contains($0) }
        }
    }
    

    If the array will have a lot of elements (and the elements are Hashable), it would likely be more efficient as:

    extension Array where Element : Hashable {
        func highestPriority(priority: [Element]) -> Element? {
            let set = Set(self)
            return priority.first { set.contains($0) }
        }
    }