在集合中使用 KeyPaths
根据键路径对集合排序和映射。
在集合中使用 KeyPaths 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。
键路径在集合中大放异彩
集合操作(如 map、sorted 和 filter)通常只需读取每个元素的一个属性。键路径让您无需编写闭包即可表达这一意图。
Swift 接受键路径出现在任何需要一个返回值的单参数函数的位置,因为键路径可以用作从 Root 到 Value 的函数。
struct Person {
let name: String
let age: Int
}
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let names = people.map(\.name)
print(names)使用键路径简写的 map
传递 map(\.property) 等同于编写一个返回该属性的闭包,但写法更短,也更清晰。开头的反斜杠和点号让编译器能够推断元素类型。
比较下面两种形式;它们会产生相同的结果。
struct Person {
let name: String
let age: Int
}
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let closureForm = people.map { $0.age }
let keyPathForm = people.map(\.age)
print(closureForm)
print(keyPathForm)使用键路径比较的 sorted(by:)
要按某个属性排序,您仍需编写一个小型比较闭包,但可以在其中通过键路径读取该属性,使意图更清晰。
这里我们通过比较每个人的 age 值,按从年轻到年长的顺序排列人员。
struct Person {
let name: String
let age: Int
}
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let path = \Person.age
let sorted = people.sorted { $0[keyPath: path] < $1[keyPath: path] }
print(sorted.map(\.name))可复用的排序辅助函数
您可以编写一个通用辅助函数,按照任意可比较属性对任意数组进行排序,并将该属性作为键路径参数传入。这样,在整个代码库中按字段排序都可以写成一行代码。
约束 T: Comparable 确保该属性可以进行排序。
func sorted<E, T: Comparable>(_ array: [E], by path: KeyPath<E, T>) -> [E] {
array.sorted { $0[keyPath: path] < $1[keyPath: path] }
}
struct Person { let name: String; let age: Int }
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
print(sorted(people, by: \.age).map(\.name))通过键路径进行筛选
对于 filter,您需要一个返回 Bool 的谓词,因此,只有当属性本身已经是 Bool 时,单独的属性键路径才能直接使用。
这里每个任务都有一个 isDone 标志,我们使用键路径简写根据它进行筛选。
struct Task {
let title: String
let isDone: Bool
}
let tasks = [Task(title: "A", isDone: true), Task(title: "B", isDone: false)]
let finished = tasks.filter(\.isDone)
print(finished.map(\.title))筛选非 Bool 属性
按非 Bool 属性筛选时,请在闭包中通过键路径读取该属性并进行比较。通用辅助函数可以为任何可相等比较的属性封装这一模式。
这里我们只保留 city 与目标相匹配的人员。
struct Person { let name: String; let city: String }
func filter<E, T: Equatable>(_ array: [E], _ path: KeyPath<E, T>, equals value: T) -> [E] {
array.filter { $0[keyPath: path] == value }
}
let people = [Person(name: "Ana", city: "Oslo"), Person(name: "Ben", city: "Rome")]
print(filter(people, \.city, equals: "Oslo").map(\.name))通过键路径使用 max 和 min
max(by:) 和 min(by:) 方法与 sorted 一样,都接受比较闭包。通过键路径读取属性,即可找到具有最大值或最小值的元素。
这里我们找出年龄最大的人。
struct Person { let name: String; let age: Int }
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25), Person(name: "Cy", age: 41)]
let path = \Person.age
if let oldest = people.max(by: { $0[keyPath: path] < $1[keyPath: path] }) {
print(oldest.name)
}使用 reduce 对属性求和
您可以将 map(\.property) 与 reduce 结合起来,计算数值字段的总和。先将每个元素转换为所需的数字,再把它们相加。
这里我们在一个流程中对所有年龄求和。
struct Person { let name: String; let age: Int }
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let totalAge = people.map(\.age).reduce(0, +)
print(totalAge)按键路径分组
Dictionary(grouping:by:) 会根据闭包的结果将元素分桶。通过键路径读取属性,即可按该字段进行分组。
这里我们按 city 将人员分组,得到一个由数组组成的字典。
struct Person { let name: String; let city: String }
let people = [
Person(name: "Ana", city: "Oslo"),
Person(name: "Ben", city: "Rome"),
Person(name: "Cy", city: "Oslo")
]
let path = \Person.city
let byCity = Dictionary(grouping: people, by: { $0[keyPath: path] })
print(byCity["Oslo"]?.map(\.name) ?? [])按多个字段排序
要先按一个字段排序,并在出现相同值时使用另一个字段打破平局,请先比较主要键路径;当主要值相等时,再回退到次要键路径。
这里我们先按 age 排序,年龄相同时再按姓名排序。
struct Person { let name: String; let age: Int }
let people = [
Person(name: "Zoe", age: 30),
Person(name: "Ana", age: 30),
Person(name: "Ben", age: 25)
]
let sorted = people.sorted {
if $0[keyPath: \Person.age] != $1[keyPath: \Person.age] {
return $0.age < $1.age
}
return $0.name < $1.name
}
print(sorted.map(\.name))为什么这里键路径优于闭包
在集合调用中使用键路径可以减少样板代码,避免闭包主体中出现拼写错误,并清楚表达您的意图:您要投影出单个字段。
对于复杂的转换,闭包仍然是合适的工具;但对于简单的属性读取,\.property 是最清晰的选择。
struct Product { let name: String; let price: Double }
let products = [Product(name: "Pen", price: 2), Product(name: "Pad", price: 5)]
let names = products.map(\.name)
let prices = products.map(\.price)
print(names)
print(prices)快速检查:map 中的键路径
请选择从每个元素中提取一个属性的简洁键路径形式。
回顾:集合中的键路径
您已经了解了键路径如何让集合代码更加简洁:
map(\.property)无需闭包即可投影出单个字段。filter(\.boolProperty)可直接用于 Bool 字段;对于其他字段,请使用带有[keyPath:]的闭包。- 排序、
max和min会在比较闭包中通过键路径读取属性。 - 接收
KeyPath的通用辅助函数,可以将按字段排序和筛选变成可复用的单行代码。
接下来您将学习可写键路径,它们不仅能读取值,还能设置值。
struct Person { let name: String; let age: Int }
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let youngestFirst = people.sorted { $0[keyPath: \Person.age] < $1[keyPath: \Person.age] }
print(youngestFirst.map(\.name))用 AI 导师学习 Swift — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 122
- 课程
- 409
常见问题解答
「在集合中使用 KeyPaths」课时是免费的吗?
是的 — 「在集合中使用 KeyPaths」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。
「在集合中使用 KeyPaths」这节课中我会学到什么?
根据键路径对集合排序和映射。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「在集合中使用 KeyPaths」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- KeyPath 基础
- 在集合中使用 KeyPaths
- 可写键路径与引用键路径
- @dynamicMemberLookup