das*_*ist 14 arrays sorting swift
假设我有一个自定义类的数组[Player],每个数组都包含一个名为的字符串属性player.position
我也有一个任意的值数组positionOrders,如下所示:
let positionOrders = ["QB", "WR", "RB", "TE"]
我的目标是[Player]先将所有"QB" 排序,然后是"WR","RB",最后是"TE".
我正在做的当前方式循环遍历每个元素positionOrders,然后在内部循环遍历所有玩家以附加到新数组.但是,我无法想出一种更简单(更有效)的方法.非常感谢任何提示或指示.谢谢.
Ale*_*ica 30
对于小尺寸Collection,这是一种可行的方法:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个更复杂的解决方案,对于更大尺寸的解决方案来说会更快 Array.firstIndex(of:)
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"]
Run Code Online (Sandbox Code Playgroud)
这两种解决方案都假设所有可能的玩家位置都根据positionOrders具有定义的顺序,因此它们O(array.count)用于简洁.如果不是这样,请告诉我.
小智 8
基于亚历山大的答案,我实施了一个扩展来做到这一点.
extension Array where Element == String {
func reordered() -> [String] {
let defaultOrder = ["orange", "pear", "watermelon", "grapefruit", "apple", "lemon", "tomatoes"]
return self.sorted { (a, b) -> Bool in
if let first = defaultOrder.index(of: a), let second = defaultOrder.index(of: b) {
return first < second
}
return false
}
}
let arrayToSort = ["lemon", "watermelon", "tomatoes"]
let sortedArray = arrayToSort.reordered()
print(sortedArray) // ["watermelon", "lemon", "tomatoes"]
Run Code Online (Sandbox Code Playgroud)
SwifterSwift有这个实现
/// SwifterSwift: Sort an array like another array based on a key path. If the other array doesn't contain a certain value, it will be sorted last.
///
/// [MyStruct(x: 3), MyStruct(x: 1), MyStruct(x: 2)].sorted(like: [1, 2, 3], keyPath: \.x)
/// -> [MyStruct(x: 1), MyStruct(x: 2), MyStruct(x: 3)]
///
/// - Parameters:
/// - otherArray: array containing elements in the desired order.
/// - keyPath: keyPath indiciating the property that the array should be sorted by
/// - Returns: sorted array.
func sorted<T: Hashable>(like otherArray: [T], keyPath: KeyPath<Element, T>) -> [Element] {
let dict = otherArray.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset }
return sorted {
guard let thisIndex = dict[$0[keyPath: keyPath]] else { return false }
guard let otherIndex = dict[$1[keyPath: keyPath]] else { return true }
return thisIndex < otherIndex
}
}
Run Code Online (Sandbox Code Playgroud)
positionOrders.filter{player.contains($0)}
Run Code Online (Sandbox Code Playgroud)
这里的所有都是它的。
排序的方式就像这样:
player.sort{
.. your formula for sorting goes here
}
Run Code Online (Sandbox Code Playgroud)
例如,如果您想按“银行余额”排序
player.sort{
$0.bankBalance < $1.bankBalance
}
Run Code Online (Sandbox Code Playgroud)
要按“另一个数组”排序,只需
player.sort{
let i = positionOrders.firstIndex(of: $0) ?? 0
let j = positionOrders.firstIndex(of: $1) ?? 0
return i < j
}
Run Code Online (Sandbox Code Playgroud)
(请注意,如果您希望“丢失”的项目出现在末尾而不是开头,如果您的用例中可以想象丢失的项目,那么显然您只需使用而不是使用。Int.max)0
(请注意,此页面上有关性能的评论偏离了很多很多数量级。在所述环境中,性能可能是令人难以置信的问题。如果令人难以置信地,令人难以置信的是,性能是一个问题,那么您只需执行显然,添加一行代码来散列索引。不过要小心,在几乎所有情况下,进行散列实际上!!性能较差!!再说一次,只是 TBC,性能不太可能成为问题;使用上面的代码)
小智 7
这是一个基于OuSS代码的通用Swift 4解决方案,要求数组元素是相等的。
extension Array where Element: Equatable {
func reorder(by preferredOrder: [Element]) -> [Element] {
return self.sorted { (a, b) -> Bool in
guard let first = preferredOrder.index(of: a) else {
return false
}
guard let second = preferredOrder.index(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"]
Run Code Online (Sandbox Code Playgroud)
我将要做的:
positionOrders并获取每个键(位置)的值。这是代码:
let preSortPlayerList = [Player]() // Filled with your players.
let positionOrders = ["QB", "WR", "RB", "TE"]
let dict = preSortPlayerList.reduce([String : [Player]]()) {
var map = $0
if var tmp = map[$1.position] {
tmp.append($1)
map[$1.position] = tmp
} else {
map[$1.position] = [$1]
}
return map
}
let playersArray: [Player] = positionOrders.flatMap { dict[$0] ?? [Player]() }
print("\(playersArray)")
Run Code Online (Sandbox Code Playgroud)