Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

143
Views
¿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 answers
Answer question

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 Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!