hi, I have to paginate some web api and stop after...
# announcements
b
hi, I have to paginate some web api and stop after a request returns “more”: false. I’m currently looking at sequences and takeWhile stops immediately after the predicate returns false; is there a way to say: take from this sequence until you encounter something including that thing? Only solution I can think of is using a mutable local variable and set that to false after the takeWhile
r
is the client requesting a list of elements?
results.takeWhile { it.more }
b
right, but the last result has it.more as false and includes elements as well
r
not sure what your data looks like but that code i posted will break once condition is not met
b
so if I paginated by 10 elements and had 18 elements in total, the last request would return more: false and would include 8 elements
results.takeWhile { it.more } would skip the remaining 8 elements right?
k
how are you getting more items from api?
b
by making another request with an offset
k
this takeWhile you want to achieve "should" be bound to take/do more requests
yes but how does the pipeline look like?
b
you start at offset 0 and send a size as well, e.g. 10
you get the 10 first items with more: true
then you make another request with offset 10 and size 10
then you get the remaining 8 items and more: false
k
yes I understand that
but do you have some concrete code?
b
yep, but it’s a bit abstracted, sec
Copy code
val hasMore = true
generateSequence(0, { it + maximumPageSize })
        .takeWhile { hasMore }
        .map { offset ->
            val end = offset + maximumPageSize
            val targetFileName = "${offset}-${end}__${download.targetFileName}"
            val paginatedDownload = download.copy(
                    targetFileName = targetFileName,
                    urlParameters = paginateUrlParameters(offset)
            )
            val importer = HttpImporter(paginatedDownload)
            importer.run(targetDirectory)
        }
the next map will parse the json and set the hasMore variable
have to implement that
and maybe there’s an issue with the generateSequence, have to check if the seed is sent as the initial element or if the lambda is executed first
ideally I could just use something like: .endSequenceAfter { it.hasMore }
and that would keep the first element where the predicate is false
k
I can't come up with other solution than having mutable variable as you have
r
Would this be useful?
Copy code
data class Page<T>(
  val elements: List<T>,
  val offset: Int,
  val hasMore: Boolean
)

val allThings: Sequence<Thing> = 
  generateSequence(getPage(0)) { page ->
    if (page.hasMore)
      getPage(page.offset + page.elements.size)
    else null
  }
  .flatMap { it.elements.asSequence() }