前言
作为开发人员,我们经常需要处理需要数小时甚至数天复杂业务算法(各种for循环
)。由于swift
的高阶函数功能,如map
,reduce
,filter
,等,其中的一些复杂的算法,现在可以很容易地通过短短的几行代码解决。
在本文中,我想向您展示 5 种以前难以实现的算法
,现在非常容易实现,这要归功于 Swift
中的高阶函数
。
在本文中,我将使用以下students
数组作为模型对象,以便您更好地了解这些高阶函数
的工作原理。
let students = [
Student(id: "991", name: "Jessica", gender: .female, age: 20),
Student(id: "992", name: "James", gender: .male, age: 25),
Student(id: "993", name: "Mary", gender: .female, age: 19),
Student(id: "994", name: "Edwin", gender: .male, age: 27),
Student(id: "995", name: "Stacy", gender: .female, age: 18),
Student(id: "996", name: "Emma", gender: .female, age: 22),
]
enum Gender {
case male
case female
}
struct Student {
let id: String
let name: String
let gender: Gender
let age: Int
}
计算数组元素的出现次数
计算数组中元素的总数很容易,但是如果我们想根据某些条件计算元素的出现次数怎么办?例如,假设我们想统计students数组中有多少男学生
和女学生
(Gender
属性)。
我们可以利用数组的reduce(into:)功能。我们来看看下面的示例代码:
let genderCount = students.reduce(into: [Gender: Int]()) { result, student in
guard var count = result[student.gender] else {
// Set initial value to `result`
result[student.gender] = 1
return
}
// Increase counter by 1
count += 1
result[student.gender] = count
}
let femaleCount = genderCount[.female]! // Output: 4
let maleCount = genderCount[.male]! // Output: 2
上面的示例代码所做的是将students
数组缩减为一个类型为 的字典``[Gender: Int]
。在闭包
中,我们通过计算男女学生的出现次数
来累积填充最终字典。
复习Swift
reduce
函数
我们先来复习一下 reduce
函数 , reduce函数是,一个可以设置一个初始值
的函数,并且可以返回两个结果变量
,我们一般称为result
,currentCase
result
: 一般是指上次得到的结果之和
currentCase
: 一般指本次遍历的对象
举例:
let prices = [20,30,40]
let sum = prices.reduce(0) { $0 + $1 }
print(sum)
//90
注意: reduce(0)
这里reduce(0)
是什么初始值,我们函数就会返回什么结果
好,返回上面的函数,我们要统计
students数组中有多少男学生
和女学生
[Gender: Int]()
: 是一个以 Gender
为Key
, int
为值
的的字典
所以我们得以 key
Gender
来统计累加count
,男生女生总数,来分别计算出男女生的总数.
我们打印的log po genderCount
▿ 2 elements
▿ 0 : 2 elements
- key : SwiftUnitTest.Gender.female
- value : 4
▿ 1 : 2 elements
- key : SwiftUnitTest.Gender.male
- value : 2
获取数组的总和
接下来,我想向您展示如何仅用 1 行代码获得数组的总和。假设我们想要获得学生年龄的总和。为此,我们可以reduce(_:_:)
像这样使用数组的函数:
let sum = students.reduce(0) { result, student in
return result + student.age
}
可能你已经猜到了,我们可以通过使用简写参数名称
来进一步简化
上述示例代码:
let sum = students.reduce(0, { $0 + $1.age })
对于数组元素类型是支持加法运算符 ( +)
的类型,我们可以通过省略简写参数名称
来进一步简化
它:
let sum1 = [2, 3, 4].reduce(0, +) // Output: 9
let sum2 = [5.5, 10.7, 9.43].reduce(0, +) // Output: 44.435
let sum3 = ["a","b","c"].reduce("", +) // Output: "abc"
reduce 是很好用的,在计算数值,和拼接字符串的时候.
从数组中获取一些随机元素
从数组中获取一些随机元素曾经是一种难以实现的算法,因为我们需要处理各种边缘情况。
现在借助Swift
数组中的shuffled()
和 prefix(_:)
函数,这个操作变得非常容易实现。
以下是从students
数组中随机挑选 3
名学生的方法:
// Randomize all elements within the array
let randomized = students.shuffled()
// Get the first 3 elements in the array
let selected = randomized.prefix(3)
let selected2 = randomized.prefix(13)
print(selected)
print(selected2)
这种方法的一个好处是,即使我们尝试获取
的元素数量超过
数组的总
元素,它也不会触发索引
超出范围异常。
结果
[SwiftUnitTest.Student(id: "996", name: "Emma", gender: SwiftUnitTest.Gender.female, age: 22),
SwiftUnitTest.Student(id: "991", name: "Jessica", gender: SwiftUnitTest.Gender.female, age: 20),
SwiftUnitTest.Student(id: "992", name: "James", gender: SwiftUnitTest.Gender.male, age: 25)]
[SwiftUnitTest.Student(id: "996", name: "Emma", gender: SwiftUnitTest.Gender.female, age: 22),
SwiftUnitTest.Student(id: "991", name: "Jessica", gender: SwiftUnitTest.Gender.female, age: 20),
SwiftUnitTest.Student(id: "992", name: "James", gender: SwiftUnitTest.Gender.male, age: 25),
SwiftUnitTest.Student(id: "995", name: "Stacy", gender: SwiftUnitTest.Gender.female, age: 18),
SwiftUnitTest.Student(id: "993", name: "Mary", gender: SwiftUnitTest.Gender.female, age: 19),
SwiftUnitTest.Student(id: "994", name: "Edwin", gender: SwiftUnitTest.Gender.male, age: 27)]
我们可以看到随机可以随到最大的数6
就结束了,并没有一定要完成到 13
按条件对数组元素进行分组
假设我们想按student
name
的第一个字母
对学生进行分组。传统上,我们必须手动遍历数组中的每个元素, 并相应地对它们进行分组。
struct Student {
let id: String
let name: String
let gender: Gender
let age: Int
}
现在,在Dictionary(grouping:by:)
初始化程序的帮助下,我们可以在不使用for-in
循环的情况下实现这一点。就是这样:
let groupByFirstLetter = Dictionary(grouping: students) { student in
return student.name.first!
}
/*
PrintLog:
[
["J": [SwiftUnitTest.Student(id: "991", name: "Jessica", gender: SwiftUnitTest.Gender.female, age: 20), SwiftUnitTest.Student(id: "992", name: "James", gender: SwiftUnitTest.Gender.male, age: 25)],
"M": [SwiftUnitTest.Student(id: "993", name: "Mary", gender: SwiftUnitTest.Gender.female, age: 19)],
"E": [SwiftUnitTest.Student(id: "994", name: "Edwin", gender: SwiftUnitTest.Gender.male, age: 27), SwiftUnitTest.Student(id: "996", name: "Emma", gender: SwiftUnitTest.Gender.female, age: 22)],
"S": [SwiftUnitTest.Student(id: "995", name: "Stacy", gender: SwiftUnitTest.Gender.female, age: 18)]]
]
*/
从上面的示例代码中可以看出,初始化程序将生成一个类型为 的字典[KeyType: Student]
。
KEY = 每个Student
的name
的第一个字母 ,
如果我们想按标准对学生
进行分组
, 并在多部分表格视图
中显示他们,这将特别有用。
我们甚至可以通过Swift
中使用速记参数名称或者健路语法来进一步简化
// Using shorthand argument names
let groupByFirstLetter = Dictionary(grouping: students, by: { $0.name.first! })
// Using key path syntax
let groupByFirstLetter = Dictionary(grouping: students, by: \.name.first!)
总结
上面显示的所有示例绝对可以通过使用传统for-in
循环来解决。但是,这需要我们手动处理各种边缘情况,因此非常容易出错。
通过使用高阶函数
,我们可以大大降低代码的复杂性
,从而减少出错的可能性
。最重要的是,它使我们的代码更易于维护。
因为这个部分比较多,我后面应该也还会分享一些, 其他的高阶函数
使用方法,或者如果你有什么好的使用场景,也可以留言,我们互相学习,哈哈哈~ 周末愉快!