簡體   English   中英

對自定義類型的 Swift 數組進行排序

[英]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"

我如何對這些進行排序,以便從該數組返回的前三個項目始終是LondonLiverpoolManchester 最后 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM