基于另一个数组的顺序对数组进行高效sorting

假设我有这个:

struct Pet { let name: String } let pets = [Pet(name: "Z"), Pet(name: "F"), Pet(name: "A"), Pet(name: "E")] let petNames = ["E", "F", "Z", "A"] 

我的预期输出是:

 [Pet(name: "E"), Pet(name: "F"), Pet(name: "Z"), Pet(name: "A")] 

如何按照petNames的订单高效地分类pets

我目前的方法似乎是非常低效的:

 var sortedPets = [Pet]() for n in petNames { sortedPets.append(pets.first(where: { return $0.name == n })!) } 

任何function的方法,我可以使用?

效率不高,但它在function上解决了问题:

 let pets2 = pets.sorted{petNames.index(of:$0.name)! < petNames.index(of:$1.name)!} 

现在我们知道我们在做什么了,这个更精细,但是效率更高,因为字典查询是快速的:

 var d = [String:Int]() zip(petNames, 0..<petNames.count).forEach { d[$0.0] = $0.1 } let pets2 = pets.sorted{d[$0.name]! < d[$1.name]!} 

我为此写了一个扩展。 它非常灵活,它可以让你根据什么字段来定义input的sorting方式,以及如何根据sortOrder列表来处理没有定义sorting顺序的元素。

这是相当长的,所以我build议你把它放在自己的文件中:

 enum UnspecifiedItemSortingPolicy { case first case last case omitEntirely case assertAllItemsHaveDefinedSorting } extension MutableCollection { typealias Element = Iterator.Element func sorted<T: Equatable>( byOrderOf sortingMemberDeriver: @escaping (Element) -> T, in sortingOrder: [T], sortUnspecifiedItems unspecifiedItemSortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting ) -> [Element] { switch unspecifiedItemSortingPolicy { case .omitEntirely: return self .lazy .map { (element: Element) -> (element: Element, position: Int) in let sortingMember = sortingMemberDeriver(element) guard let position = sortingOrder.index(of: sortingMember) else { fatalError("Attempted to sort a collection (\(self)) containing an item (\(element)) whose ordering was not defined in the sorting order: \(sortingOrder).") } return (element: element, position: position) } .sorted{ $0.position < $1.position } .map{ $0.element } case .assertAllItemsHaveDefinedSorting: return self .lazy .flatMap { (element: Element) -> (element: Element, position: Int)? in let sortingMember = sortingMemberDeriver(element) return sortingOrder.index(of: sortingMember).map{ (element: element, position: $0) } } .sorted{ $0.position < $1.position } .map{ $0.element } case .first, .last: var unspecifiedItems = Array<Element>() //items whose ordering isn't in the sortingOrder let sortedPortion = self.flatMap { (element: Element) -> (element: Element, position: Int)? in let sortingMember = sortingMemberDeriver(element) guard let position = sortingOrder.index(of: sortingMember) else { unspecifiedItems.append(element) return nil } return (element: element, position: position) } .sorted{ $0.position < $1.position } .map{ $0.element } switch unspecifiedItemSortingPolicy { case .first: return unspecifiedItems + sortedPortion case .last: return sortedPortion + unspecifiedItems default: fatalError("This should never happen.") } } } } extension MutableCollection where Iterator.Element: Equatable { func sorted( byOrderIn sortingOrder: [Element], sortUnspecifiedItems unspecifiedItemSortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting ) -> [Element] { return self.sorted(byOrderOf: {$0}, in: sortingOrder, sortUnspecifiedItems: unspecifiedItemSortingPolicy) } } 

从那里,用法是非常简单和优雅的:

 struct Pet { let name: String } let pets = [Pet(name: "Z"), Pet(name: "F"), Pet(name: "A"), Pet(name: "E")] let petNames = ["E", "F", "Z", "A"] let sorted = pets.sorted(byOrderOf: { $0.name }, in: petNames, sortUnspecifiedItems: .last) sorted.forEach{ print($0) }