La mayoría de operadores de filtrado tienen una versión con prefijo try que recibe un closure que puede arrojar un error. Si esto ocurre (arrojar el error), el Publisher terminará y emitirá un evento de fin fallido (.failure) con el error arrojado en el closure.
Prinicipios de filtrado
filter recibe un predicado (closure) que retorna un Bool, y solo deja pasar los valores donde el predicado sea true.
let numbers = (1...10).publisher
numbers
.filter { $0.isMultiple(of: 3) }
.sink { ... } // 3, 6, 9
removeDuplicates() vuelve a emitir los eventos del "stream" de entrada, y elimina aquellos eventos consecutivos que estén duplicados. No recibe parámetros, pero requiere que los eventos sean comparables, o sea que conformen el protocolo Equatable. En caso contrario, se puede usar un predicado que retorna un Bool para definir la condición de igualdad entre dos entradas.
let numbers = [1, 1, 2, 3, 3].publisher
numbers
.removeDuplicates()
.sink { ... } // 1, 2, 3
Compactar (eliminar nulos)
compactMap(_:) recibe un closure para hacer una transformación que retorna un opcional. Luego, a diferencia de map(_:), filtra todos los valores nulos y devuelve los restantes como requeridos (i.e. no opcionales).
let strings = ["a", "1", "2", "b", "3"].publisher
strings
.compactMap { Int($0) }
.sink { ... } // 1, 2, 3
Ignorar eventos
ignoreOutput() ignora todos los eventos emitidos en el "stream" de entrada y solo re-emite el evento de fin, sea fallido o exitoso.
let numbers = (1...10_000).publisher
numbers
.ignoreOutput()
.sink(...) // finished
Encontrando valores
first(where:) emite el primer valor que cumple con la condición del predicado. Tan pronto encuentra este valor, emite un evento de fin, razón por lo cual se lo considera perezoso ("lazy"). También es posible usar el operador first() (sin argumentos) que retorna el primer elemento del "stream" de entrada.
let numbers = (1...9).publisher
numbers
.first(where: { $0 % 2 == 0 })
.sink(...)
// first even value is 2
// completed with: finished
last(where:) emite el último valor que cumple con la condición del predicado. Este operador espera hasta recibir un evento de fin en el "stream" de entrada, lo que significa que la secuencia DEBE ser finita. También se puede usar el operador last() (sin argumentos) que retorna el último elemento del "stream" de entrada.
let numbers = (1...9).publisher
numbers
.last(where: { $0 % 2 == 0 })
.sink(...)
// last even value is 8
// completed with: finished
Descartando valores iniciales
dropFirst(_:) omite el número de elementos especificado por el argumento count (que vale 1, por defecto), y luego re-emite el resto de valores recibidos en el "stream" de entrada.
let numbers = (1...9).publisher
numbers
.dropFirst(8)
.sink(...)
// 9
// 10
drop(while:) omite todos los eventos del "stream" de entrada mientras que el predicado sea verdadero. Luego, re-emite el resto de valores recibidos. El closure predicado de este operador solo se ejecuta mientras el resultado sea true. Después de retornar false por primera vez, el closure no se vuelve a ejecutar.
let numbers = (1...7).publisher
numbers
.drop(while: { $0 % 5 != 0 })
.sink(...)
// 5
// 6
// 7
drop(untilOutputFrom:) ignora los valores emitidos por el "stream" de entrada hasta que reciba el primer elemento del segundo "stream". Un ejemplo de caso de estudio de este operador podría ser que un usuario presione un botón, pero que yo decida ignorar esos llamados hasta que el publisher isReady emita un resultado.
let isReady = PassthroughSubject<Void, Never>()
let taps = PassthroughSubject<Int, Never>()
// Se crea una suscripción, ignorando los valores de taps
// hasta recibir el primer evento de isReady
taps
.drop(untilOutputFrom: isReady)
.sink( ... )
// Después de recibir el tercer evento de taps,
// isReady envía su evento.
(1...5).forEach { n in
taps.send(n)
if n == 3 { isReady.send() }
}
Descartando valores finales
prefix(_:) re-emite los valores recibidos en el "stream" de entrada hasta que completa la cantidad (amount) que fue definida por parámetro. Luego, emite un evento de fin (.finished), razón por la que se lo considera perezoso.
let numbers = (1...9).publisher
numbers
.prefix(2)
.sink(...)
// 1
// 2
// Completed with: finished
prefix(while:) re-emite los valores recibidos en el "stream" de entrada mientras que un predicado se cumpla. Tan pronto el predicado se vuelve falso, se emite un evento de fin (.finished).
let numbers = (1...9).publisher
numbers
.prefix(while: { $0 % 4 != 0 })
.sink(...)
// 1
// 2
// 3
// Completed with: finished
prefix(untilOutputFrom:) re-emite los valores recibidos en el "stream" de entrada hasta que un segundo Publisher emite su primer valor.
let isReady = PassthroughSubject<Void, Never>()
let taps = PassthroughSubject<Int, Never>()
// Se crea una suscripción, re-emitiendo los valores de taps
// hasta recibir el primer evento de isReady
taps
.prefix(untilOutputFrom: isReady)
.sink( ... )
// Después de recibir el segundo evento de taps,
// isReady envía su evento.
(1...5).forEach { n in
taps.send(n)
if n == 2 { isReady.send() }
}
Cuestionario
1. ¿Qué sucede si un operador con prefijo try arroja un error dentro de su closure?
2. ¿Cuál es la diferencia fundamental entre map y compactMap?
3. ¿Por qué last(where:) requiere necesariamente que la secuencia sea finita?
4. ¿Qué ocurre con los valores ignorados por ignoreOutput()?
5. ¿Qué condición debe dejar de cumplirse para que drop(while:) empiece a reenviar valores?
6. filter permite…
- [ ] Transformar valores opcionales en no opcionales
- [ ] Solo dejar pasar valores que cumplen un predicado
- [ ] Ignorar todos los valores y solo emitir el fin
- [ ] Emitir solo el primer valor recibido
7. removeDuplicates() requiere que los valores sean:
- [ ]
Identifiables - [ ]
Codable - [ ]
Equatable - [ ]
Hashable
8. ¿Qué operador emite solo el primer valor que cumple una condición y luego termina?
- [ ] prefix(while:)
- [ ] first(where:)
- [ ] dropFirst(_:)
- [ ] prefix(_:)
9. ¿Qué operador omite valores mientras un predicado sea verdadero?
- [ ]
drop(while:) - [ ]
prefix(while:) - [ ]
ignoreOutput() - [ ]
compactMap(_)
10. prefix(untilOutputFrom:) se detiene cuando…
- [ ] El predicado deja de cumplir
- [ ] El Publisher de entrada termina
- [ ] Se recibe el primer valor del segundo Publisher
- [ ] Se detecta un valor duplicado
Solución
¿Qué sucede si un operador con prefijo try arroja un error dentro de su closure?
Se da por terminada la suscripción y se emite un evento de fin fallido (
.failure) con el error arrojado desde el closure.
2. ¿Cuál es la diferencia fundamental entre map y compactMap?
maptransforma un valor recibido en otro.compactMapaplica una función de transformación sobre un flujo de datos, que puede resultar en valores opcionales. Todos los nulos son filtrados y se terminan emitiendo valores de tipo requerido.
3. ¿Por qué last(where:) requiere necesariamente que la secuencia sea finita?
last(where:)debe analizar todos los valores emitidos en el flujo de datos, así que espera recibir un evento de fin exitoso, para luego emitir su valor.
4. ¿Qué ocurre con los valores ignorados por ignoreOutput()?
ignoreOutput()no emite ningún valor. Solo posiblemente un evento de fin, si el flujo de datos de entrada lo emite.
5. ¿Qué condición debe dejar de cumplirse para que drop(while:) empiece a reenviar valores?
Cuando el predicado de
drop(while:)sea true, empezará a reenviar valores.
6. filter permite…
- [✅] Solo dejar pasar valores que cumplen un predicado
7. removeDuplicates() requiere que los valores sean:
- [✅]
Equatable
8. ¿Qué operador emite solo el primer valor que cumple una condición y luego termina?
- [✅]
first(where:)
9. ¿Qué operador omite valores mientras un predicado sea verdadero?
- [✅]
drop(while:)
10. prefix(untilOutputFrom:) se detiene cuando…
- [✅] Se recibe el primer valor del segundo
Publisher
Top comments (0)