Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

139
Vistas
¿Cómo limitar una secuencia de Kotlin con un predicado que se preocupa por el siguiente elemento?

Necesito abordar una API que devuelve datos fragmentados.

 val response = server.getChunk(request, chunkNumber) data class PageInfo( val pageNumber: Int, val maxPages: Int ) data class Response( val elements: List<Payload>, val pageInfo: PageInfo )

Supongo que esta API es idempotente.

Puedo hacer algo así ( toma 1 ):

 var i = 0 var maxPages: Int val payloadTotal = mutableListOf<Payload>() do { val response = server.getChunk(request, i++) maxPages = response.pageInfo.maxPages payloadTotal.addAll(response.payload) } while (i < maxPages)

Funciona, pero es feo.

tomar 2 :

 val partners = IntStream .iterate(0) { it + 1 } .asSequence() .map { server.getChunk(request, it) } .takeWhile { it.pageInfo.pageNumber < it.pageInfo.maxPages } .map { it.payload } .flatten() .toList()

Se ve mucho mejor, pero no funciona del todo como se esperaba: dado que verificamos el predicado takeWhile después de solicitar el servidor, siempre terminaremos haciendo una solicitud adicional. Y dado que no conocemos el valor de maxPages antes de hacer la primera solicitud, no podemos usar algo como take(n: Int) .

Así que terminé yendo con ese código ( toma 3 ):

 var maxPages = Int.MAX_VALUE val partners = IntStream .iterate(0) { it + 1 } .asSequence() .takeWhile { it < maxPages } .map { server.getChunk(request, it) .also { response -> maxPages = response.pageInfo.maxPages } } .map { it.payload } .flatten() .toList()

Funciona. Pero, de nuevo, usamos este estado adicional que no me gusta.

Además, debido a las restricciones "efectivamente definitivas", esto no funcionará en Java en absoluto (y me gustaría que lo hiciera como bonificación).

Entonces, ¿cómo logro el comportamiento requerido con un enfoque funcional? ¿Hay alguna manera de hacerlo sin rastrear ningún estado adicional fuera de la canalización?

*Si tal forma existe, ¿también se traduce a Java?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Podemos crear nuestro propio operador de secuencia para este propósito. Requiere escribir algo de código, pero luego podemos usarlo muy bien:

 generateSequence(0) { it + 1 } .map { server.getChunk(request, it) } .takeNextWhile { it.pageInfo.pageNumber + 1 < it.pageInfo.maxPages } .flatMap { it.elements } .toList() fun <T> Sequence<T>.takeNextWhile(predicate: (T) -> Boolean): Sequence<T> = Sequence { val iter = iterator() var hasNext = iter.hasNext() object : Iterator<T> { override fun hasNext() = hasNext override fun next() = iter.next().also { hasNext = iter.hasNext() && predicate(it) } } }

Alternativamente, podemos takeWhilePrevious() si eso tiene más sentido para nosotros.

Si no queremos crear nuevos operadores, creo que es mejor no usar transformaciones funcionales, ya que empeoran la legibilidad del código en este caso. Suponiendo que maxPages no cambie, primero podemos obtenerlo y luego realizar un ciclo muy simple:

 val first = server.getChunk(request, 0) val payloads = first.elements.toMutableList() (1 until first.pageInfo.maxPages).forEach { payloads += server.getChunk(request, it).elements }
over 4 years ago · Santiago Trujillo Denunciar

0

Pequeña solución para que funcione, puede pasar el objeto de respuesta nula como semilla para generateSequence

 val partners = generateSequence(0 to (null as Response?)) { (page, _) -> page+1 to getChunk(page) }.takeWhile { (page, response) -> response == null || page < response.maxPages }.flatMap { it.second?.payload ?: emptyList() }.toList()

(Sin embargo, el objetivo de legibilidad aún no está resuelto). Se puede introducir cierta claridad usando funciones

 data class PageResponse(val page: Int = 0, val response: Response? = null) { fun isNotLastPage() = response == null || page < response.maxPages fun getPayload() = response?.payload ?: emptyList() } val partners = generateSequence(PageResponse()) { (page, _) -> PageResponse(page + 1, getChunk(page)) } .takeWhile(PageResponse::isNotLastPage) .flatMap(PageResponse::getPayload) .toList()
over 4 years ago · Santiago Trujillo Denunciar

0

En este caso, probablemente haría una secuencia a partir de un bucle do-while while. Es bastante legible para aquellos que necesitan leer la implementación y bastante útil para aquellos que solo se preocupan por la secuencia:

 fun Server.requestInPages(request: Request, startPage: Int = 0): Sequence<Response> = sequence { var page = startPage do { val response = getChunk(request, page++) yield(response) } while (response.pageInfo.pageNumber < response.pageInfo.maxPages) }

Y luego utilízalo así:

 val partners = server.requestInPages(request) .flatMap { it.elements } .toList()

Si las respuestas son todas iguales, incluso podría agregar otro asistente que contenga flatMap encima de requestInPages . Si a nadie le importa la estructura de respuesta y PageInfo en absoluto, incluso podría hacer que flatMap parte de requestInPages .

Puntos de bonificación: esto incluso podría convertirse en un flujo si su llamada API se convierte en una función de suspend en algún momento (por ejemplo, si cambia a rutinas).

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda