KeyPath的使用范例

141 阅读3分钟

截屏2024-01-03 07.05.58.png

案例一

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) { 0+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[keyPath:keyPath],default:[]].append(0[keyPath: keyPath], default: []].append(0)

        }

        return result

    }

}

// ✨ 請使用以下兩個變數進行分類,並印出回傳的字典。

// spends 請分別印出用「付款方式」和用「類別」分類的方式

print(spends.groupBy(.付款方式))

print(spends.groupBy(.類別))

// movies 請用「類型」分類

print(movies.groupBy(.類型))