假设我有这个:
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")]
如何按照pets
'订单有效地对petNames
进行排序?
我目前的方法似乎非常低效:
var sortedPets = [Pet]()
for n in petNames {
sortedPets.append(pets.first(where: { return $0.name == n })!)
}
我可以使用的任何功能方法吗?
答案 0 :(得分:1)
效率不高,但它在功能上解决了这个问题:
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]!}
答案 1 :(得分:1)
我为此写了一个扩展名。它非常灵活,因为它允许您定义输入排序的字段,以及如何处理排序顺序未根据sortingOrder列表定义的元素。
它很长,所以我建议你把它藏在自己的文件中:
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) }