一、前言

  • 在一个条件或者单个属性上进行排序非常简单, Swift 本身就有相关的功能。
  • 如下所示,对 int 数组进行排序的例子:
let numbers = [3, 5, 6, 1, 8, 2]
let sortedNumbers = numbers.sorted { (lhs, rhs) inreturn lhs < rhs
}// [1, 2, 3, 5, 6, 8]
  • 但有时需要根据多个条件或属性来进行排序,那么该怎么处理呢?为了演示这一点,我们可以创建一个结构体来说明。如下所示,现有一个简单的 BlogPost 结构体,它包含帖子标题和两个统计数据,即浏览次数 pageView 和会话持续时间 sessionDuration:
struct BlogPost {let title: Stringlet pageView: Intlet sessionDuration: Double
}
  • Sample 数据:
extension BlogPost {static var examples: [BlogPost] = [BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3),BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1),BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2),BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10)]
}
  • 如果想查看哪些文章表现良好,可以按照浏览次数对它们直接进行排序,但是很多帖子都不那么流行,页面浏览量也一样。在这种情况下,需要根据另一个条件或属性来进行进一步的排序。下面来分析一下多属性排序,它们有各种各样的方法来解决这个问题,这里只展示没有任何复杂概念的最基本的方法,一旦了解了基本原理,就可以随心所欲地进阶。

二、多条件排序

  • 多条件排序是指比较第一个条件的排序,只有当第一个条件相等时,才转到下一个条件,直到找到一个不相等的条件。
  • 伪代码如下所示:
let sortedObjects = objects.sorted { (lhs, rhs) infor (lhsCriteria, rhsCriteria) in [(lhsCrtria1, rhsCriteria1), (lhsCrtria2, rhsCriteria2), (lhsCrtria3, rhsCriteria3), ... , (lhsCrtriaN, rhsCriteriaN)] { // ① if lhsCriteria == rhsCriteria {  // ②continue}return lhsCriteria < rhsCriteria // ③}
}
    • ① 从最重要的一个(也就是第一个)开始,循环遍历条件列表;
    • ② 如果这个顺序条件相等,不能根据它来决定顺序,就跳到下一个条件;
    • ③ 如果可以根据条件决定两个对象之间的顺序,就停止并返回结果。

三、按照两个字段对 object 数组进行排序

  • 使用前面提到的场景,我们希望根据表现对 BlogPost 进行排序,表现取决于页面浏览次数 pageView,如果浏览次数相同,再看 sessionDuration。
  • 如下所示,是上一个例子中用到的 BlogPost 结构体和对应的 sample 数据:
struct BlogPost {let title: Stringlet pageView: Intlet sessionDuration: Double
}extension BlogPost {static var examples: [BlogPost] = [BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3),BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1),BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2),BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10)]
}
  • 衡量表现的方法可以翻译成下面这样的代码:
let popularPosts = BlogPost.examples.sorted { (lhs, rhs) in if lhs.pageView == rhs.pageView { // ① return lhs.sessionDuration > rhs.sessionDuration }return lhs.pageView > rhs.pageView // ②
}
    • ① 如果博客文章有相同的访问次数,使用访问时间;
    • ② 如果访问次数不相等,可以直接根据访问次数来排序(使用降序)。
  • 排序的结果:
[BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2.0),
BlogPost(title: "Abena",   pageView: 4, sessionDuration: 10.0),
BlogPost(title: "Alice",   pageView: 1, sessionDuration: 3.0),
BlogPost(title: "Peter",   pageView: 1, sessionDuration: 2.0),
BlogPost(title: "Kofi",    pageView: 1, sessionDuration: 1.0)]

四、按照多个字段对 object 数组进行排序

  • 不难发现,根据两个条件来排序非常简单,让我们引入更多的条件,如果博客文章的表现相同,按照 title 排序,添加更多的 sample 数据:
extension BlogPost {static var examples2: [BlogPost] = [BlogPost(title: "Zoo",    pageView: 5, sessionDuration: 2),BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3),BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1),BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2),BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10),BlogPost(title: "Angero", pageView: 1, sessionDuration: 2)]
}
  • 两个条件和三个条件没什么区别,可以沿用相同的逻辑:
let popularPosts = BlogPost.examples2.sorted { (lhs, rhs) inif lhs.pageView == rhs.pageView {// 添加另一个if来检查博客文章是否具有相同的会话持续时间,如果它们具有相同的页面浏览次数和会话持续时间,则按标题对它们进行排序if lhs.sessionDuration == rhs.sessionDuration { return lhs.title < rhs.title}       return lhs.sessionDuration > rhs.sessionDuration}return lhs.pageView > rhs.pageView
}
  • 排序结果:
[BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2.0),BlogPost(title: "Zoo",    pageView: 5, sessionDuration: 2.0),BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10.0),BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3.0),BlogPost(title: "Angero", pageView: 1, sessionDuration: 2.0),BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2.0),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1.0)]
  • 可以对两个和三个条件使用相同的逻辑,这里唯一的问题是,条件越多,需要的嵌套就越多。如下所示,这是一个多条件的例子,可能会导致 pyramid of doom:
let popularPosts = BlogPost.examples2.sorted { (lhs, rhs) inif lhs.pageView == rhs.pageView {if lhs.sessionDuration == rhs.sessionDuration { if lhs.nextCriteria == rhs.nextCriteria { if lhs.nextCriteria == rhs.nextCriteria { ....}...}...}   return lhs.sessionDuration > rhs.sessionDuration}return lhs.pageView > rhs.pageView
}

五、按照 N 个字段对 object 数组进行排序

  • 为了避免 pyramid of doom,再看看之前的伪代码:
let sortedObjects = objects.sorted { (lhs, rhs) infor (lhsCriteria, rhsCriteria) in [(lhsCrtria1, rhsCriteria1), (lhsCrtria2, rhsCriteria2), (lhsCrtria3, rhsCriteria3), ... , (lhsCrtriaN, rhsCriteriaN)] {if lhsCriteria == rhsCriteria {continue}return lhsCriteria < rhsCriteria}
}
  • 上面的代码不是解决类似问题的唯一方式,不过关键思路是相似的,关键思路就是把多个条件打包到一个集合当中去遍历:
extension BlogPost {static var examples2: [BlogPost] = [BlogPost(title: "Zoo",    pageView: 5, sessionDuration: 2),BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3),BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1),BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2),BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10),BlogPost(title: "Angero", pageView: 1, sessionDuration: 2)]
}typealias AreInIncreasingOrder = (BlogPost, BlogPost) -> Bool // ①let popularPosts = BlogPost.examples2.sorted { (lhs, rhs) in    let predicates: [AreInIncreasingOrder] = [ // ②{ $0.pageView > $1.pageView },{ $0.sessionDuration > $1.sessionDuration},{ $0.title < $1.title }]for predicate in predicates { // ③if !predicate(lhs, rhs) && !predicate(rhs, lhs) { // ④continue // ⑤}return predicate(lhs, rhs) // ⑥}return false
}
  • 分析说明:
    • ① 声明一个别名 AreInIncreasingOrder 用来匹配排序闭包,这可以提高对谓词集合声明的可读性;
    • ② 声明一个谓词集合;
    • ③ 遍历这个谓词集合;
    • ④ 这里是关键逻辑,想要检查条件是否能决定博文顺序,但是 AreInIncreasingOrder 返回了一个布尔值,我们应该如何判断它们是否相等? 先来看看定义,AreInIncreasingOrder 是一个谓词,它会在第一个参数能决定顺序时返回 true 否则返回 false,两个变量只有在各自都不是升序时才相等;这意味着无论参数顺序如何,谓词都必须是 false,换言之 lhs.pageView < rhs.pageView 和 rhs.pageView < lhs.pageView 必须等于 false 才能决定顺序相等,这就是 !predicate(lhs, rhs) && !predicate(rhs, lhs) 这句代码的意思;
    • ⑤ 如果顺序相等,那么 continue 到下一个谓词;
    • ⑥ 如果顺序不相等,那么可以用这个谓词来排序。
  • 排序结果:
[BlogPost(title: "Akosua", pageView: 5, sessionDuration: 2.0), BlogPost(title: "Zoo",    pageView: 5, sessionDuration: 2.0), BlogPost(title: "Abena",  pageView: 4, sessionDuration: 10.0), BlogPost(title: "Alice",  pageView: 1, sessionDuration: 3.0), BlogPost(title: "Angero", pageView: 1, sessionDuration: 2.0), BlogPost(title: "Peter",  pageView: 1, sessionDuration: 2.0),BlogPost(title: "Kofi",   pageView: 1, sessionDuration: 1.0)]

Swift之深入解析如何进行多重条件排序相关推荐

  1. Comparator.comparing嵌套对象倒序以及多重条件排序

    一.嵌套对象倒序的正确书写方式 描述:对象A内部封装对象B,根据B的字段做倒排 Comparator<User> ageDescCom = Comparator.comparing(tem ...

  2. 数组多重筛选条件排序方法

    根据一个或者多个属性对数组进行排序,支持嵌套的属性.而且可以在每个条件中指定排序的方向,并支持传入比较函数. 安装 采用 npm 安装: $ npm install --save arr-sort 复 ...

  3. Swift之深入解析如何使用Xcode和LLDB v2修改UI元素

    一.前言 在上一篇博客中,已经详细地介绍如何使用 LLDB 表达式修改 UI 元素,具体请参考:Swift之深入解析如何将代码添加为自定义LLDB命令. 在这篇博客中,将继续讨论相同的问题需求,并将重 ...

  4. Swift之深入解析如何避免单元测试中的强制解析

    一.前言 强制解析(使用 !)是 Swift 语言中不可或缺的一个重要特点(特别是和 Objective-C 的接口混合使用时),它回避了一些其他问题,使得 Swift 语言变得更加优秀. 比如在我的 ...

  5. python中多重if语句用法_python-循环语句的简单条件语句、多重条件语句和嵌套条件语句编写...

    1. 简单if-else语句 例1:要求用户输入两个数,计算两数的和.如果这两个数的和大于100,则输出"两数和大于100",否则输出"两数和小于100". 程 ...

  6. Excel多重条件IF语句应用

    Excel多重条件IF语句 IF(条件1,值,IF(条件2,值,IF(条件3,值))) IF(条件1,值,IF(条件2,值,其余情况的值)) 通过以上方式可以进行多重条件的嵌套 1.根据前文的数据进行 ...

  7. VB6基本数据库应用(七):多重条件搜索

    同系列的第七篇,上一篇在:http://blog.csdn.net/jiluoxingren/article/details/48402835 多重条件搜索 前文再续,书接上一回.很高兴又能说出这句话 ...

  8. 2021-09-02-python多重条件if判断输入分数是否及格

    python多重条件if判断输入分数是否及格 #!/usr/bin/python3 score = int(input('分数: ')) #交互式输入分数,并使用int()转换成数值,赋值给变量sco ...

  9. Swift 类似HandyJSON解析Struct

    Swift 类似HandyJSON解析Struct HandyJSON 从源码解析Struct 获取TargetStructMetadata 获取TargetStructDescriptor 实现Ta ...

最新文章

  1. Eclipse启动失败:No java virtual machine was found after searching the follwing locations
  2. 百度、谷歌理念对对碰
  3. VTK:可视化之CurvatureBandsWithGlyphs
  4. 建模大师怎么安装到revit中_全面解析Revit软件在装配式建筑项目中的建模思路...
  5. 十六进制、RGB 与 VBA颜色值对照表
  6. C++设计模式-抽象工厂模式
  7. 爬虫使用urllib库报错urllib.error.URLError: 「urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate veri
  8. 天堂2单机版如何架设mysql_天堂2单机版 L2J-som-rotm 安装全过程及常用工具 一步到位...
  9. 监控工具Prometheus服务在K8s上异常
  10. matlab计算恒向线航程
  11. MADlib——基于SQL的数据挖掘解决方案(7)——数据转换之其它转换
  12. VS2008下VLC播放器,实现播放、暂停、停止、快进、截图、进度条显示、进度条控制功能
  13. android仿百度新闻,【Android】最新主流新闻app功能实现。仿网易,搜狐等新闻客户端实现展示...
  14. 网络本科计算机统考操作题,计算机本科统考复习操作题操作步骤.doc
  15. 有可以提醒自己上下班打卡的手机便签软件吗?
  16. 我的世界服务器修改皮肤指令,我的世界怎么拿指令来改皮肤的 | 手游网游页游攻略大全...
  17. 赛灵思 PL 和 PS IBIS 模型解码器
  18. 浅入浅出linux中断子系统
  19. php 招聘要求 转载
  20. 拓扑排序在实际项目中应用

热门文章

  1. Logdump使用指引
  2. 20175213 2018-2019-2 《Java程序设计》第6周学习总结
  3. AutoComplete - 自动完成插件
  4. Image-Based Aging Using Evolutionary Computing (EURO 2008)
  5. java屏蔽关键字_替换禁用语(指定关键字)的过滤器
  6. C语言入门题-计算指数 (15分)
  7. CSS样式为什么放在head中,而不放在body底部
  8. android+qq登录测试,对于android的第三方(QQ登录,微信登录等的)测试时的签名配置...
  9. Java黑皮书课后题第11章:11.2(Person Student Employee Faculty Staff类)设计一个名为Person的类及其两个名为Student和Employee的子类
  10. C语言学习之用筛选法求100之内的素数