map (keyPath), где keyPath - это переменная
let arr = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
arr.map(\.0) // [1, 2, 3, 4, 5]
Работает отлично. Но приведенный ниже код не компилируется:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Невозможно преобразовать значение типа WritableKeyPath <(Int, Int), Int> в ожидаемый тип аргумента, выбрасываемое ((Int, Int)) -> T.
Не удалось вывести общий параметр "T".
Ответы
Array.mapожидает закрытия с подписью (Element) throws -> T.
В Swift 5.2 ключевые пути можно было передавать как функции / замыкания (вот предложение эволюции ), но только как литералы (по крайней мере, согласно предложению, он говорит «пока», так что, возможно, это ограничение будет снято. ).
Чтобы преодолеть это, вы можете создать расширение, Sequenceкоторое принимает ключевой путь:
extension Sequence {
func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
return map { $0[keyPath: keyPath] }
}
}
(кредит: https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/)
Тогда вы можете делать то, что хотели:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Предложение эволюция показала , как сделать это с оператором, но вы также можете использовать тот же []или ()синтаксис ли он частично применяется или нет, так как индексы и функции не нужны аргументы.
let oneTo5 = 1...5
let keyPath = \(Int, Int).0
XCTAssert(
zip(oneTo5, oneTo5).map(keyPath[]).elementsEqual(oneTo5)
)
let keyPath = \Double.isZero
XCTAssertFalse(keyPath[1.0]())
public extension KeyPath {
/// Convert a `KeyPath` to a partially-applied get accessor.
subscript() -> (Root) -> Value {
{ $0[keyPath: self] } } /// Convert a `KeyPath` to a get accessor. subscript(root: Root) -> () -> Value { { root[keyPath: self] } } } public extension ReferenceWritableKeyPath { /// Convert a `KeyPath` to a partially-applied get/set accessor pair. subscript() -> (Root) -> Computed<Value> { { self[$0] }
}
/// Convert a `KeyPath` to a get/set accessor pair.
subscript(root: Root) -> Computed<Value> {
.init(
get: self[root],
set: { root[keyPath: self] = $0 }
)
}
}
/// A workaround for limitations of Swift's computed properties.
///
/// Limitations of Swift's computed property accessors:
/// 1. They are not mutable.
/// 2. They cannot be referenced as closures.
@propertyWrapper public struct Computed<Value> {
public typealias Get = () -> Value
public typealias Set = (Value) -> Void
public init(
get: @escaping Get,
set: @escaping Set
) {
self.get = get
self.set = set
}
public var get: Get
public var set: Set
public var wrappedValue: Value {
get { get() }
set { set(newValue) }
}
public var projectedValue: Self {
get { self }
set { self = newValue }
}
}
//MARK:- public
public extension Computed {
init(
wrappedValue: Value,
get: @escaping Get = {
fatalError("`get` must be assigned before accessing `wrappedValue`.")
},
set: @escaping Set
) {
self.init(get: get, set: set)
self.wrappedValue = wrappedValue
}
}