2

I am working on a PlayFramework application written in Scala.

Problem is that in a rest controller I need a list of elements (books) and for each element list of its subelements (chapters).

Book repository:

def findAll(): Future[Seq[Book]] 

Chapter repository:

def findByBookId(bookId: UUID): Future[Seq[Chapter]]

I wanted to do something like

val books = bookRepository.findAll
val result = for {
  bookList <- books
  book <- bookList
  chapters <- chapterRepository.findByBookdId(book.id)
} yield (book, chapters)

I want to have a tuple of book and its chapters so I can latter map it to a json. What I am doing wrong, because I get error:

[error]  required: scala.collection.GenTraversableOnce[?]

Or what would be a better approach how to iterate over future of collection and for each element load another future of collection?

martyn
  • 136
  • 1
  • 8
  • 1
    You can't mix up what you're iterating through in a for comprehension. For example, if you try to say `Seq[Thing] <- Future[Seq[Thing]]` and `Thing <- [Seq[Thing]` in the same for comprehension it will fall over and cause this (rather unhelpful) GenTraversableOnce error. – James Whiteley Sep 18 '18 at 09:04

3 Answers3

3

See if this gets you close to what you're after.

// found books (future)
val fBooks :Future[Seq[Book]] = bookRepository.findAll()

// chapters in fBooks order (future)
val fChptrs :Future[Seq[Seq[Chapter]]] = fBooks.flatMap {books =>
  Future.sequence(books.map(book =>findByBookId(book.id)))
}

// combine book with its chapters
val result :Future[Seq[(Book, Seq[Chapter])]] = for {
  books <- fBooks
  chptrs <- fChptrs
} yield books zip chptrs
jwvh
  • 50,871
  • 7
  • 38
  • 64
0

You can only use one type of monad in a for comprehension, since it is just syntactic sugar for flatMap and map.

See here the whole description: https://stackoverflow.com/a/33402543/2750966

pme
  • 14,156
  • 3
  • 52
  • 95
0

You should use the same type throughout the for-comprehension.

You can't have books:Future[Seq[Books] and bookList: Seq[Book] in same for-comprehension.

Also you can convert Seq[Future[Something]] to Future[Seq[Something]] using Future.sequence

So, something like this should work

val res: Future[(Seq[Book], Seq[Chapter])] =
for {
  bookList <- books
  chapters <- Future.sequence(bookList.map(book => findByBookId(book.id)))
} yield (bookList, chapters.flatten)
Ale
  • 946
  • 4
  • 17
  • 28