【问题标题】:Consume "paginated" (not really) API in Scala with unknown page count and unknown page size在 Scala 中使用未知页数和未知页大小的“分页”(不是真的)API
【发布时间】:2021-07-26 15:05:04
【问题描述】:

假设您使用的 API 最多可为您提供 100 个元素,并允许您获取更多元素,而不是通过指定页码,而只需告诉它您收到的最后一个元素。然后你会想要抽象这个准分页。 你会如何在 Scala 中做到这一点?

我想出了这段可能有问题(= 未经过彻底测试)且非常有状态的代码:

abstract class IteratorThatKeepsOnGiving[T] extends Iterator[T] {
  private var currentIter: Iterator[T] = _
  private var currentElement: T = _

  def nextIterator(lastElement: Option[T]): Iterator[T]

  fetchNewBatch(None)

  def fetchNewBatch(lastElement: Option[T]) = {
    currentIter = nextIterator(lastElement)
  }

  def hasNext: Boolean = currentIter.hasNext || {
    fetchNewBatch(Some(currentElement))
    currentIter.hasNext
  }

  def next(): T = {
    currentIter.nextOption() match {
      case Some(value) =>
        currentElement = value
        value
      case None =>
        fetchNewBatch(Some(currentElement))
        next()
    }
  }
}

我不喜欢它。

用途:

val numberIterator = new IteratorThatKeepsOnGiving[Int] {
  def nextIterator(lastElement: Option[Int]): Iterator[Int] = {
    val i = lastElement.getOrElse(-1)
    ((i + 1) to (i + 4)).iterator
  }
}

必须有更好的方法来做到这一点。当然,我不是第一个被这种分页困扰的人。无论如何,正确的术语是什么?以及如何对其进行抽象?

有问题的 API 是 Discord 的消息列表 API:https://discord.com/developers/docs/resources/channel#get-channel-messages

考虑的替代方案

我发现了这个问题:Making a Scala Iterator for a Paginated API

但是,就我而言,我没有总页数。 这个答案还假设页面大小是恒定的,虽然在我的情况下给出了这个,但我真的更喜欢一个解决方案,它可以只知道“我得到的最后一个元素是 X。我怎样才能得到 some 更多元素?”而不是“我上次得到 X,我如何得到下一个 Y 元素?”。此外,这无关紧要,因为我不能直接指定页码。

Akka Streams 中还有 flatMapConcat(和 mapConcat)也遇到了同样的问题。

【问题讨论】:

标签: scala pagination discord


【解决方案1】:

嗯,你可以做这样的事情......它看起来更漂亮/更聪明,但在你迭代时将整个页面的元素保留在内存中,而不是一次只有一个(虽然作为分页器,这可能很好反正已经持有对它们的引用)。

 Iterator
   .iterate(fetchNewBatch(None).to(LazyList)) { 
      fetchNewBatch(_.lastOption).to(LazyList)
   }.takeWhile(_.nonEmpty)
    .flatten

【讨论】:

  • 非常有趣! .iterate!所以这就是它的名字。我记得标准库中有类似的东西,但我很快就停止了寻找。很高兴看到有一个提供先前值的版本。这非常有帮助!
  • LazyList 的原因是什么?你不能这样做:val iter: Iterator[Seq[Msg]] = Iterator.iterate(nextBatchOfMessages(None))(lastList => nextBatchOfMessages(lastList.lastOption)); val iter2: Iterator[Msg] = iter.flatMap(_.iterator) 吗?
  • 好吧,我不知道你的nextBatch 会返回什么。我不想把它限制在IterableOnce之外。还有.flatMap(_.iterator) === .flatten。而且您确实需要takeWhile,因为没有它,您将在完成后重新开始。
  • 很好的答案,它完全利用了为此而设计的功能。来自文档:def iterate[T](start: T)(f: (T) => T): Iterator[T] Creates an infinite iterator that repeatedly applies a given function to the previous result.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 2019-01-20
  • 1970-01-01
相关资源
最近更新 更多