[英]Sort Swift array of custom types
我有一個包含 5 個自定義對象的數組(所有對象都具有相同的自定義類型City
)。 下面是在我的 xcode 終端中輸入po cities
的輸出:
▿ 5 elements
▿ 0 : City
▿ name : Name
- rawValue : "Manchester"
▿ description : Optional<String>
- some : "Old Trafford"
▿ 1 : City
▿ name : Name
- rawValue : "London"
▿ description : Optional<String>
- some : "Emirates"
▿ 2 : City
▿ name : Name
- rawValue : "Liverpool"
▿ description : Optional<String>
- some : "Anfield"
▿ 3 : City
▿ name : Name
- rawValue : "Newcastle"
▿ description : Optional<String>
- some : "St James Park"
▿ 4 : City
▿ name : Name
- rawValue : "Norwich"
▿ description : Optional<String>
- some : "Carrow Road"
我如何對這些進行排序,以便從該數組返回的前三個項目始終是London
、 Liverpool
、 Manchester
? 最后 2 個元素的順序在這里無關緊要。
我已經嘗試通過它們當前的數組位置來強制元素的順序,即強制City[0]
移動到City[2]
- 但是這不合適,因為我在City
接收元素的順序可以改變 - 位置需要根據特定City
rawValue
的輸出進行設置
TIA
假設您想根據原始值對城市進行排序:
let sortedCities = cities.sorted { $0.name.rawValue < $1.name.rawValue }
sortedCities.forEach {print($0)}
但是,您說您希望它們按 London 、Liverpool、Manchester 的順序排序,這不是按字母順序排列的。
如果您對 Name 值有一些任意排序順序,我建議使用排序索引字典:
enum Name: String {
case Manchester
case London
case Liverpool
case Newcastle
case Norwich
}
let sortIndexes: [Name: Int] = [.London:0,
.Liverpool:1,
.Manchester: 2,
.Newcastle: 3,
.Norwich: 4,
]
struct City: CustomStringConvertible {
let name: Name
let info: String?
var description: String {
let info = self.info ?? "nil"
return "City(name: \(self.name.rawValue), info: \(info))"
}
}
let cities = [
City(name: .London, info: "Emirates"),
City(name: .Liverpool, info: "Anfield"),
City(name: .Manchester, info: "Old Trafford"),
City(name: .Newcastle, info: "St James Park"),
City(name: .Norwich, info: "Carrow Road"),
]
let sortedCities = cities.sorted { sortIndexes[$0.name]! < sortIndexes[$1.name]! }
sortedCities.forEach {print($0)}
或者,您可以使您的枚舉類型為 Int,並使用描述來映射到顯示名稱,如 Leo 的評論中所建議的。
這是該方法的工作示例代碼:
enum Name: Int, CustomStringConvertible {
case london
case liverpool
case manchester
case newcastle
case norwich
var description: String {
switch self {
case .london:
return "London"
case .liverpool:
return "Liverpool"
case .manchester:
return "Manchester"
case .newcastle:
return "Newcastle"
case .norwich:
return "Norwich"
}
}
}
struct City: CustomStringConvertible {
let name: Name
let info: String?
var description: String {
let info = self.info ?? "nil"
return "City(name: \(self.name.description), info: \(info))"
}
}
let cities = [
City(name: .london, info: "Emirates"),
City(name: .liverpool, info: "Anfield"),
City(name: .manchester, info: "Old Trafford"),
City(name: .newcastle, info: "St James Park"),
City(name: .norwich, info: "Carrow Road"),
]
let sortedCities = cities.sorted { $0.name.rawValue < $1.name.rawValue }
sortedCities.forEach {print($0)}
由於您不需要對整個數組進行排序,而只需確保數組頂部特定元素的順序,因此最好只提取感興趣的元素並使用這些元素在頂部和其余保持不變。
這樣它的復雜度為 O(n)(假設頂部的自定義排序相對較小,因此可以假設為常數),因此對於較大的數組,它應該比執行完整數組排序更快。
一般方法是:
let unsorted: [City] = ...
let topOrder = ["London", "Liverpool", "Manchester"]
var top: [String: City] = [:]
var rest: [City] = []
for city in unsorted {
if topCorder.contains(city.name) {
top[city.name] = city
} else {
rest.append(city)
}
}
let sorted = topOrder.compactMap{ top[$0] } + rest
這不處理重復的情況(它會丟棄它們),但確實處理缺少頂級城市的情況。
從 Swift 5.3 開始,可以為 CaseIterable 自動合成 Comparable。
import Foundation
struct City {
enum Name: CaseIterable, Comparable {
case london
case liverpool
case manchester
case newcastle
case norwich
}
let name: Name
}
extension City.Name: RawRepresentable {
init?(rawValue: String) {
guard let name = ( Self.allCases.first { $0.rawValue == rawValue } )
else { return nil }
self = name
}
var rawValue: String { "\(self)".capitalized }
}
AnyIterator { }
.prefix(5)
.map { City.Name.allCases.randomElement()! }
.map(City.init)
.sorted(\.name)
public extension Sequence {
/// Sorted by a common `Comparable` value.
func sorted<Comparable: Swift.Comparable>(
_ comparable: (Element) throws -> Comparable
) rethrows -> [Element] {
try sorted(comparable, <)
}
/// Sorted by a common `Comparable` value, and sorting closure.
func sorted<Comparable: Swift.Comparable>(
_ comparable: (Element) throws -> Comparable,
_ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
) rethrows -> [Element] {
try sorted {
try areInIncreasingOrder(comparable($0), comparable($1))
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.