简体   繁体   English

对自定义类型的 Swift 数组进行排序

[英]Sort Swift array of custom types

I have an array of 5 custom objects (all objects are of the same custom type, City ).我有一个包含 5 个自定义对象的数组(所有对象都具有相同的自定义类型City )。 The below is the output of typing po cities in my xcode terminal:下面是在我的 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"

How can I sort these so the first three items returned from this array are always London , Liverpool , Manchester ?我如何对这些进行排序,以便从该数组返回的前三个项目始终是LondonLiverpoolManchester The final 2 elements order is irrelevant here.最后 2 个元素的顺序在这里无关紧要。

I've tried forcing the order of the elements by their current array position, ie forcing City[0] to move to City[2] - however this isn't suitable as the order I receive the elements within City can change - the position needs to be set based on the output of the specific City rawValue我已经尝试通过它们当前的数组位置来强制元素的顺序,即强制City[0]移动到City[2] - 但是这不合适,因为我在City接收元素的顺序可以改变 - 位置需要根据特定City rawValue的输出进行设置

TIA TIA

Assuming you want to sort your cities based on raw value:假设您想根据原始值对城市进行排序:

let sortedCities = cities.sorted { $0.name.rawValue < $1.name.rawValue }
sortedCities.forEach {print($0)} 

However, you say you want them sorted in the order London , Liverpool, Manchester, which is not alphabetical.但是,您说您希望它们按 London 、Liverpool、Manchester 的顺序排序,这不是按字母顺序排列的。

Edit:编辑:

If you have some arbitrary sort order for your Name values, I suggest using a dictionary of sort indexes:如果您对 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)}

Alternately, you can make your enums be type Int, and use a description to map to display names, as suggested in Leo's comment.或者,您可以使您的枚举类型为 Int,并使用描述来映射到显示名称,如 Leo 的评论中所建议的。

Here is working example code for that approach:这是该方法的工作示例代码:

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)}

Since you don't need to sort the entire array, but just ensure the order of specific elements at the top of the array, it might be best to just extract the elements of interest and construct a new array with those elements at the top and with the rest left unchanged.由于您不需要对整个数组进行排序,而只需确保数组顶部特定元素的顺序,因此最好只提取感兴趣的元素并使用这些元素在顶部和其余保持不变。

This way it has an O(n) complexity (assuming the custom sorting of the top is relatively small so can be assumed to be a constant), so for larger arrays it should be faster than doing the full array sorting.这样它的复杂度为 O(n)(假设顶部的自定义排序相对较小,因此可以假设为常数),因此对于较大的数组,它应该比执行完整数组排序更快。

The general approach would be:一般方法是:

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

This doesn't handle the case of duplicates (it will drop them), but does handle the case of missing top cities.这不处理重复的情况(它会丢弃它们),但确实处理缺少顶级城市的情况。

Comparable can be automatically synthesized for CaseIterable as of Swift 5.3.从 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