map (keyPath) donde keyPath es una variable
let arr = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
arr.map(\.0) // [1, 2, 3, 4, 5]
Funciona genial. Pero el siguiente código no se compila:
let keyPath = \(Int, Int).0
arr.map(keyPath)
No se puede convertir el valor del tipo 'WritableKeyPath <(Int, Int), Int>' al tipo de argumento esperado '((Int, Int)) arroja -> T'.
No se pudo inferir el parámetro genérico 'T'.
Respuestas
Array.mapespera un cierre con firma (Element) throws -> T.
En Swift 5.2, se permitía que las rutas clave se pasaran como funciones / cierres (aquí hay una propuesta de evolución ), pero solo como literales (al menos, según la propuesta, dice "por ahora", por lo que tal vez se levante esta restricción ).
Para superar esto, puede crear una extensión Sequenceque acepte una ruta clave:
extension Sequence {
func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
return map { $0[keyPath: keyPath] }
}
}
(crédito a: https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/)
Entonces puedes hacer lo que quisieras:
let keyPath = \(Int, Int).0
arr.map(keyPath)
La propuesta de evolución mostró cómo hacerlo con un operador, pero también se puede usar la misma sintaxis []o ()si se aplica parcialmente o no, porque los subíndices y las funciones no necesitan argumentos.
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
}
}