「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
在我们的日常开发中,经常会碰到一些复杂的数据操作的需求。这些复杂的需求可能会花费我们半天甚至几天的时间趋势线。
但是如果我们能利用 Swift 本身提供的高阶函数来去实现的话,不仅能降低项目的复杂度,还能使代码更加易读高效。
如果你对 Swift 的高阶函数的使用不太了解的话,推荐先去看一下我之前写的一篇文章:高阶函数的使用。
在本篇文章中,将会介绍 5 个通过高阶函数来进行简化的复杂的数据操作需求。Let's go!
Prepare
假设,我们有一个 Person 的结构体如下:
struct Person {
enum Gender {
case male
case female
}
var name = ""
var age = 0
var gender = Gender.female
}
下面是我们需要操作的数据源,后面的需求都在此数据源上操作:
let dataSource = [Person(name: "鸡大宝", age: 38, gender: .male),
Person(name: "江主任", age: 50, gender: .female),
Person(name: "可乐", age: 10, gender: .female),
Person(name: "伍六七", age: 16, gender: .male),
Person(name: "梅花十三", age: 20, gender: .female)]
获取数据源中男女各多少人
如果不适用高阶函数的话,我们可能会按照下面的思路来去获取:
- 创建两个变量 maleCount/femaleCount 来代表男女的人数。
- 遍历数组元素,在 for-loop 中添加 if-else 语句,如果男则 maleCount +1,反之则 femaleCount +1.
上面的代码虽然写起来也不复杂,但还是有 2 个小瑕疵:
- 代码不能一目了然地去知道代码的实际用途。
- maleCount/femaleCount 必须声明为 var,但其实这两个变量后续我们是不需要改动的,按道理应该声明为 let。但局限于后面的变量修改,只能声明为 var,这又会给程序带来不可控性。
这个需求可以使用高阶函数的 reduce(into:)
来实现:
let genderCount = dataSource.reduce(into: [Person.Gender: Int]()) { (result, person) in
result[person.gender, default: 0] += 1
}
let maleCount = genderCount[Person.Gender.male] // 2
let femaleCount = genderCount[Person.Gender.female] // 3
代码解释:通过 reduce(into:) 函数生成一个 [Gender: Int]
类型的字典,在 reduce 内部的进行 male 和 female 的计数。
这样也很好的解决了上面的两个问题。代码可读性很高;maleCount 与 femaleCount 也被声明为 let 。
随机获取两个元素
对于这个需求,我们可以拆分成下面的两个步骤来实现:
- 通过数据源生成一个随机数组 - randomDataSource。
- 获取 randomDataSource 的前两个元素。 转换为代码如下:
let randomDataSource = dataSource.shuffled()
let randomElement = randomDataSource.prefix(2)
查找某个特定的元素
在项目开发中,我们会经常遇到在一个数组中查找某个特定元素的需求。如果,目标数组的元素较少, for - loop 或者 filter 是最方便的。但如果目标数组很大,而我们有经常需要进行查找操作的话,上述的实现就不太满足了。
这时候,我们可以使用空间换时间的思路来解决查找时间过长的问题。主要步骤分为下面的两步:
- 现将数据源转为一个 tuple 的数组 - personTuple。0 的位置为 key,1 的位置为 person 对象。
- 再通过 Dictionary 的初始化函数:
init<S>(uniqueKeysWithValues keysAndValues: S)
将 personTuple 转为字典。 这样,通过 key 再去查找就是 O(1) 的时间了。需要注意的是将数组转为字典的过程仍然是 O(n)。
let personTuple = dataSource.map { ($0.name, $0)}
let personDict = Dictionary(uniqueKeysWithValues: personTuple)
let target = personDict["伍六七"]