ios - iOS Swift - 按枚举模式排序数组
问题描述
我有一组自定义对象。这些对象有一个自定义的枚举 var 'type'。不同的类型如下:
- 。电影
- 。电视
- 。预告片
- 。类型
- 。文章
我想按模式对数组进行排序 [电影、电视、预告片、流派、文章、电影、电视、预告片、流派、文章……等]
我已经使枚举符合可比较的但是(也许我错了),如果我按类型排序,它不会像这样对数组进行排序:
[movie, movie, movie, tv, tv, tv, trailer, trailer, trailer, etc...]
..事实上,我希望它们以一种又一种的模式出现。
[movie, tv, trailer, genre, article, movie, tv, trailer, genre, article, movie, tv, trailer, genre, article, and so on ...]
解决方案
在旧的 Swift 版本(Swift 5.2.x 或更早版本)中,当使枚举符合Comparable
协议时,您需要声明它的 rawValue Int
,String
否则它没有任何意义,因为枚举通常不是按字典顺序排序的。在Swift 5.3 或更高版本中,如果您希望它自动合成,则不能声明任何 rawValue 类型。您可以在 Swift evolution 中查看有关枚举类型的 Synthesized Comparable 一致性的这篇文章
选择加入综合 Comparable 一致性的枚举类型将根据案例声明顺序进行比较,后期案例的比较大于早期案例。只有没有关联值的枚举类型和只有 Comparable 关联值的枚举类型才有资格获得综合一致性。后一种枚举将首先按 case 声明顺序进行比较,然后按字典顺序按有效负载值进行比较。没有具有原始值的枚举类型符合条件。
Swift 5.3 或更高版本
enum Kind: Comparable {
case movie, tv, trailer, genre, article
}
完成此操作后,您可以使用我为这个问题指出为重复的自定义排序来简单地对集合进行排序:
extension MutableCollection where Self: RandomAccessCollection {
mutating func sort<T: Comparable>(_ predicate: (Element) -> T, by areInIncreasingOrder: (T, T) -> Bool = (<)) {
sort { areInIncreasingOrder(predicate($0),predicate($1)) }
}
}
extension Sequence {
func sorted<T: Comparable>(_ predicate: (Element) -> T, by areInIncreasingOrder: (T,T)-> Bool = (<)) -> [Element] {
sorted { areInIncreasingOrder(predicate($0),predicate($1)) }
}
}
游乐场测试:
struct Item {
let id: Int
let name: String
let kind: Kind
}
let items: [Item] = [
.init(id: 1, name: "D", kind: .tv),
.init(id: 2, name: "B", kind: .movie),
.init(id: 3, name: "F", kind: .trailer),
.init(id: 4, name: "H", kind: .genre),
.init(id: 5, name: "J", kind: .article),
.init(id: 6, name: "C", kind: .tv),
.init(id: 7, name: "A", kind: .movie),
.init(id: 8, name: "E", kind: .trailer),
.init(id: 9, name: "G", kind: .genre),
.init(id: 10, name: "I", kind: .article)]
items.sorted(\.kind) // [{id 2, name "B", movie}, {id 7, name "A", movie}, {id 1, name "D", tv}, {id 6, name "C", tv}, {id 3, name "F", trailer}, {id 8, name "E", trailer}, {id 4, name "H", genre}, {id 9, name "G", genre}, {id 5, name "J", article}, {id 10, name "I", article}]
编辑/更新
我不知道是否有更简单的方法来完成这种排序(我很想得到一些反馈)但是您可以按名称对项目进行排序,按种类分组,然后转置您的项目。您需要使您的枚举 CaseIterable 并将其 rawValue 声明为 Int 从零开始。因此,将这些助手添加到您的项目中:
extension Collection where Element: RandomAccessCollection, Element.Indices == Range<Int> {
func transposed() -> [[Element.Element]] {
(0..<(max(\.count)?.count ?? .zero)).map {
index in compactMap { $0.indices ~= index ? $0[index] : nil }
}
}
}
extension Sequence {
func max<T: Comparable>(_ predicate: (Element) -> T) -> Element? {
self.max(by: { predicate($0) < predicate($1) })
}
}
接着:
enum Kind: Int, CaseIterable {
case movie = 0, tv, trailer, genre, article
}
let grouped: [[Item]] = items.reduce(into: .init(repeating: [], count: Kind.allCases.count)) { result, item in
result[item.kind.rawValue].append(item)
}
let transposed = grouped.map{$0.sorted(\.name)}.transposed()
print(transposed) // [[Item(id: 7, name: "A", kind: Kind.movie), Item(id: 6, name: "C", kind: Kind.tv), Item(id: 8, name: "E", kind: Kind.trailer), Item(id: 9, name: "G", kind: Kind.genre), Item(id: 10, name: "I", kind: Kind.article)], [Item(id: 2, name: "B", kind: Kind.movie), Item(id: 1, name: "D", kind: Kind.tv), Item(id: 3, name: "F", kind: Kind.trailer), Item(id: 4, name: "H", kind: Kind.genre), Item(id: 5, name: "J", kind: Kind.article)]]
推荐阅读
- flutter - 谷歌助手打开 Flutter 应用时运行命令
- node.js - 如何通过用户名和密码保护环回资源管理器?
- python - 拆分存储在数组中的字符串值
- json - jq 在每个 json 对象之前跳过初始无效字符串并打印所选列
- java - 线程安全构建器模式
- c++ - 在 Visual Studio 2019 项目中静态链接 SDL-2 的问题
- java - 为什么我得到 java.lang.ArithmeticException: 除以零
- sql - 查询以在 IMPALA 中一起显示所有列、表和模式名称
- mysql - Docker 数据库容器未获取现有数据库中的数据
- android - 如何在 min API 21 下更改启动图标大小