カスタムクラスの配列があり[Player]
、それぞれに次の文字列プロパティが含まれているとします。player.position
またpositionOrders
、次のように、と呼ばれる任意の値の配列があります。
let positionOrders = ["QB", "WR", "RB", "TE"]
私の目標は、[Player]
最初にすべての「QB」、次に「WR」、「RB」、最後に「TE」になるように並べ替えることです。
現在の方法では、の各要素をループしpositionOrders
、その内部ですべてのプレーヤーをループして新しい配列に追加します。しかし、これを行うためのより簡単な(そしてより効率的な)方法を見つけることができませんでした。ヒントやポインタは大歓迎です。ありがとう。
編集:私の元のアプローチはたわごとでした。この投稿は多くの注目を集めたので、もう少し注意を払い、改善する時が来ました。
基本的に、問題は簡単です。2つの要素がありCollection
、相対的な順序によって並べ替え順序が決まる配列(または任意の順序付き)があります。すべての要素について、順序付けられたコレクション内でその位置を見つけ、2つのインデックスを比較して、どちらが「大きい」かを判断します。
ただし、単純に線形検索を実行すると(たとえば)、特に固定順序が非常に大きい場合は、Array.firstIndex(of:)
パフォーマンスが非常に悪くなります(O(array.count)
)。これを修正するために、Dictionary
要素をインデックスにマップするを作成できます。辞書はO(1)
、仕事に最適な高速ルックアップを提供します。
これはまさに何をするかHardCodedOrdering
です。要素の辞書をそれらの順序に事前に計算し、2つの要素を比較するためのインターフェースを提供します。さらに良いことに、順序が不明な要素の検出に対して異なる応答をするように構成できます。それらを最初に配置するか、最後に配置するか、完全にクラッシュする可能性があります(デフォルトの動作)。
HardCodedOrdering
public struct HardCodedOrdering<Element> where Element: Hashable {
public enum UnspecifiedItemSortingPolicy {
case first
case last
case assertAllItemsHaveDefinedSorting
}
private let ordering: [Element: Int]
private let sortingPolicy: UnspecifiedItemSortingPolicy
public init(
ordering: Element...,
sortUnspecifiedItems sortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
) {
self.init(ordering: ordering, sortUnspecifiedItems: sortingPolicy)
}
public init<S: Sequence>(
ordering: S,
sortUnspecifiedItems sortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
) where S.Element == Element {
self.ordering = Dictionary(uniqueKeysWithValues: zip(ordering, 1...))
self.sortingPolicy = sortingPolicy
}
private func sortKey(for element: Element) -> Int {
if let definedSortKey = self.ordering[element] { return definedSortKey }
switch sortingPolicy {
case .first: return Int.min
case .last: return Int.max
case .assertAllItemsHaveDefinedSorting:
fatalError("Found an element that does not have a defined ordering: \(element)")
}
}
public func contains(_ element: Element) -> Bool {
return self.ordering.keys.contains(element)
}
// For use in sorting a collection of `T`s by the value's yielded by `keyDeriver`.
// A throwing varient could be introduced, if necessary.
public func areInIncreasingOrder<T>(by keyDeriver: @escaping (T) -> Element) -> (T, T) -> Bool {
return { lhs, rhs in
self.sortKey(for: keyDeriver(lhs)) < self.sortKey(for: keyDeriver(rhs))
}
}
// For use in sorting a collection of `Element`s
public func areInIncreasingOrder(_ lhs: Element, rhs: Element) -> Bool {
return sortKey(for: lhs) < sortKey(for: rhs)
}
}
let rankOrdering = HardCodedOrdering(ordering: "Private", "Lieutenant", "Captain", "Admiral") // ideally, construct this once, cache it and share it
let someRanks = [
"Admiral", // Should be last (greatest)
"Gallactic Overlord", // fake, should be removed
"Private", // Should be first (least)
]
let realRanks = someRanks.lazy.filter(rankOrdering.contains)
let sortedRealRanks = realRanks.sorted(by: rankOrdering.areInIncreasingOrder) // works with mutating varient, `sort(by:)`, too.
print(sortedRealRanks) // => ["Private", "Admiral"]
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加