案例一
struct 猫咪 {
var name: String
}
let cats = [猫咪(name: "蛋蛋"), 猫咪(name: "秘密")]
print(cats.map(\.name))
var cat = 猫咪(name: "张三")
cat[keyPath: \.name] = "123"
print(cat.name)
案例二
protocol 有名字 {
static var namePath: KeyPath<Self, String> { get }
}
extension 有名字 {
func 打招呼() {
print("哈喽 \(self[keyPath: Self.namePath])")
}
}
struct 猫咪: 有名字 {
static let namePath = \猫咪.name
let name: String
}
struct 地址: 有名字 {
static let namePath = \地址.住户
let 住户: String
}
let cat = 猫咪(name: "猫咪123")
let address = 地址(住户: "住户456")
cat.打招呼()
address.打招呼()
案例三
/// 案例四
struct 猫咪 {
var name: String
var age: Int? = nil
}
let someKeyPath: KeyPath<猫咪, String> = \.name
let someClosure: (猫咪) -> String = \.name
猫咪们.map(someKeyPath)
案例四
struct 猫咪: CustomStringConvertible {
var name: String
var color: String
var description: String { "\(name) (\(color))" }
}
let 猫咪们 = [猫咪(name: "蛋蛋", color: "橘"), 猫咪(name: "橘咪", color: "白 + 黄")]
print(猫咪们.filter { $0.name.contains("橘") || $0.color.contains("橘")})
extension Array {
func filter(keyword: String, on paths: [KeyPath<Element, String>]) -> Self {
filter { element in
paths.contains { path in
let 属性资料 = element[keyPath: path]
return 属性资料.contains(keyword)
}
}
}
}
print(猫咪们.filter(keyword: "橘", on: [\.name, \.color]))
案例五
struct 猫咪: CustomStringConvertible, 可搜寻 {
static var 搜寻属性: [KeyPath<猫咪, String>] = [\.name, \.color]
var name: String
var color: String
var description: String { "\(name) (\(color))" }
}
let 猫咪们 = [猫咪(name: "蛋蛋", color: "橘"), 猫咪(name: "橘咪", color: "白 + 黄")]
protocol 可搜寻 {
static var 搜寻属性: [KeyPath<Self, String>] { get }
}
extension Sequence where Element: 可搜寻 {
func filter(keyword: String) -> [Element] {
filter { element in
Element.搜寻属性.contains { path in
let 属性资料 = element[keyPath: path]
return 属性资料.contains(keyword)
}
}
}
}
print(猫咪们.filter(keyword: "橘"))
案例六
struct 狗狗: CustomStringConvertible, 可搜寻 {
static var 搜寻属性: [KeyPath<狗狗, String>] = [\.名字, \.品种.rawValue]
enum 品种: String {
case 博美, 柴犬, 哈士奇, 米克斯
}
var 名字: String
var 品种: 品种
var 体重: Int
var description: String { "\(名字) (\(self.品种.rawValue)"}
}
let 狗狗们 = [狗狗(名字: "皮皮", 品种: .米克斯, 体重: 7000),
狗狗(名字: "米粒", 品种: .哈士奇, 体重: 7000),
狗狗(名字: "栗子", 品种: .柴犬, 体重: 7000)
]
print(狗狗们.filter(keyword: "皮皮"))
案例7
let 狗狗们 = [狗狗(名字: "皮皮", 品种: .米克斯, 体重: 7000),
狗狗(名字: "米粒", 品种: .哈士奇, 体重: 7000),
狗狗(名字: "栗子", 品种: .柴犬, 体重: 7000)
]
print(狗狗们.filter(keyword: "皮皮"))
extension Sequence {
func sorted<T: Comparable>(_ path: KeyPath<Element, T>) -> [Element] {
sorted {
$0[keyPath: path] < $1[keyPath: path]
}
}
func sum<T: AdditiveArithmetic>(_ path: KeyPath<Element, T>) -> T {
reduce(T.zero) { $0 + $1[keyPath: path] }
}
}
print(狗狗们.sorted { $0.体重 < $1.体重} )
print(狗狗们.sorted(\.体重))
//【ChaoCode】 Swift 中級篇 8:KeyPath 實作作業參考解答
//: 1. 在 Array 的 extension 中寫一個能把所有資料的某個 Double 屬性加總後算出平均值的方法。
extension Array {
func average(_ keyPath: KeyPath<Element, Double>) -> Double {
let total = self.reduce(0) { 1[keyPath: keyPath] }
return total / Double(self.count)
}
}
// ✨ 以下測試請自行完成,前面是 Array,然後是要計算哪個屬性的平均,最後 == 後面是預期結果,你只需要把中間文字的部分改成你設計的方法並放入 keyPath,然後確認兩邊相比結果是 true 即可
[100, 60, 5.0].average(.self) == 55
[長度單位(m: 3), 長度單位(m: 0.23), 長度單位(m: 935), 長度單位(m: 1130)].average(.公尺) == 517.0575
[長度單位(m: 23), 長度單位(m: 32.311), 長度單位(m: 935), 長度單位(m: 113.0)].average(.公分) == 27582.775
[長度單位(m: 9), 長度單位(m: 12321), 長度單位(m: 935), 長度單位(m: 1.130)].average(.公里) == 3.3165325
/*: 2. 在 Sequence 的 extension 中寫一個透過某個屬性分類後回傳字典的方法。
```
假設一筆資料是 [(name: "小芳", 性別: "女"), (name: "偉偉", 性別: "男"), (name: "芯宜", 性別: "女")]
這筆資料用「性別」分類的話,回傳的字典結果就會是
["女": [(name: "小芳", 性別: "女"), (name: "芯宜", 性別: "女")], "男": [(name: "偉偉", 性別: "男")]]
```
*/
extension Sequence {
func groupBy<Value: Hashable>(_ keyPath: KeyPath<Element, Value>) -> [Value: [Element]] {
var result = Value: [Element]
self.forEach {
result[0)
}
return result
}
}
// ✨ 請使用以下兩個變數進行分類,並印出回傳的字典。
// spends 請分別印出用「付款方式」和用「類別」分類的方式
print(spends.groupBy(.付款方式))
print(spends.groupBy(.類別))
// movies 請用「類型」分類
print(movies.groupBy(.類型))