Iterating on a Future[List[T]]

711 views
Skip to first unread message

Alberto Adami

unread,
Oct 3, 2014, 10:37:21 AM10/3/14
to scala...@googlegroups.com
Hi, i'm writing a Scala application.
I used a mongodb database and the reactivemongo driver.
In a call to reactivemongo i get a Future[List[Tag]], where Tag is a my type.
How can i iterate over a Future[List[T]]??
I've attempted the following code, but it doesn't compile:

var max = 0
   var tag = null
   val tags: Future[List[Tag]] = Tags.find(Json.obj()).toList
   for{
     tag <- tags
     tagsOk <- Requests.find(Json.obj("tags.tag" -> tag.category + " " + tag.name)).count
     if(tagsOk > max) {
       max = tagsOk
       tag = tag.category + " " + tag.name //string tag
     }
   }

What's wrong??

ramn.se ‘¸

unread,
Oct 3, 2014, 12:24:55 PM10/3/14
to Alberto Adami, scala-user
Looks like tag is a list of tags - tags is the future, not the list of tags

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Johannes Rudolph

unread,
Oct 3, 2014, 12:36:56 PM10/3/14
to ramn.se ‘¸, Alberto Adami, scala-user
Hi Alberto,

On Fri, Oct 3, 2014 at 6:24 PM, ramn.se ‘¸ <ram...@gmail.com> wrote:
> Looks like tag is a list of tags - tags is the future, not the list of tags

Exactly. Your `tag <- tags` line just "extracts" the List out of the
future. You need to do a second step to iterate over the list itself.
Try

val tagsFuture: Future[List[Tag]] = Tags.find(Json.obj()).toList
for{
tags <- tagsFuture
tag <- tags
...


HTH

--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

"Ionuț G. Stan"

unread,
Oct 3, 2014, 1:01:28 PM10/3/14
to Johannes Rudolph, "ramn.se ‘¸", Alberto Adami, scala-user
On 03/10/14 19:36, Johannes Rudolph wrote:
> Hi Alberto,
>
> On Fri, Oct 3, 2014 at 6:24 PM, ramn.se ‘¸ <ram...@gmail.com> wrote:
>> Looks like tag is a list of tags - tags is the future, not the list of tags
>
> Exactly. Your `tag <- tags` line just "extracts" the List out of the
> future. You need to do a second step to iterate over the list itself.
> Try
>
> val tagsFuture: Future[List[Tag]] = Tags.find(Json.obj()).toList
> for{
> tags <- tagsFuture
> tag <- tags
> ...

Unfortunately, that doesn't typecheck.

Alberto, to have access to the list, you must "enter" the future, and
that's usually achieved using either `map` or `flatMap`. Start working
with these methods before venturing into for-comprehensions. The latter
desugar to `map` and `flatMap`, which is why I recommend you to take
this path.

More to the point, to access the inner list, you'd do something like
this (notice how types change):

tagsFuture.map { (tags: List[Tag]) =>
tags.map { (tag: Tag) =>
// do something with tag
}
}

Your example isn't very self-contained and I can't really work a
solution to it. You're using a mutable `tag` variable, then try to
shadow it and I don't know what `count` returns (Int or Future[Int]).

--
Ionuț G. Stan | http://igstan.ro

Alberto Adami

unread,
Oct 5, 2014, 8:11:39 AM10/5/14
to scala...@googlegroups.com, johannes...@googlemail.com, ram...@gmail.com, alberto...@gmail.com
Thanks, now it works. Count returns a Future[Int].

Alberto Adami

unread,
Oct 6, 2014, 6:10:09 AM10/6/14
to scala...@googlegroups.com, johannes...@googlemail.com, ram...@gmail.com, alberto...@gmail.com
My code is now the following: 

def max = Action {

        var max: Int = 0

  var tagFound: Tag = null

       //obtain all the tags in the db.

       val futureTags: Future[List[Tag]] = Tags.all.toList

       val futureComputation = futureTags map{ (tags: List[Tag]) => 

  tags map {

    (tag: Tag) => 


      //create the tag String 

      val tagName = tag.category  + ":" + tag.attr 

      //search in the db the documents where tags.tag == tag.

      val futureRequests : Future[List[recommendationsystem.models.Request]]= Requests.find(Json.obj("tags.tag" -> tagName)).toList

      futureRequests map { (requests: List[recommendationsystem.models.Request]) =>

        //get the numbers of documents matching the tag

        val number: Int = requests.size

        if(number > max) {

          max = number

          tagFound = tag

        }


      }

  }

   

   

        } 

       futureComputation onComplete {

         case Success(_) => println("callback completed")

         case Failure(e) => e.printStackTrace

       }      

       println("after complete")

      val jsonObject = if(max > 0) Json.obj("tag" -> tagFound, "occurencies" -> max) else Json.obj("tag" -> "NoOne", "occurencies" -> 0)

      Ok(jsonObject)

       

     }

  

I can see that the "after complete" is printed before the  "callback completed". Why happened it?? how can i solve my new problem?


Il giorno venerdì 3 ottobre 2014 19:01:28 UTC+2, Ionuț G. Stan ha scritto:

Rafał Krzewski

unread,
Oct 6, 2014, 8:34:50 AM10/6/14
to scala...@googlegroups.com, johannes...@googlemail.com, ram...@gmail.com, alberto...@gmail.com
W dniu poniedziałek, 6 października 2014 12:10:09 UTC+2 użytkownik Alberto Adami napisał:
My code is now the following: 

...

       futureComputation onComplete {

         case Success(_) => println("callback completed")

         case Failure(e) => e.printStackTrace

       }      

       println("after complete")


I can see that the "after complete" is printed before the  "callback completed". Why happened it?? how can i solve my new problem?



This is exactly what you should expect. When your main thread calls future.onComplete(...) it registers a callback procedure, and proceeds to print "after complete". Then, when your composite future is ready, another thread invokes the body of the callback procedure. This is the whole point of using Futures!
Of course, if your main thread needs the _result_ of that Future's computation to proceed, you can use Await.result(...) to capture it. But you should look for the ways of your program in such way that your main thread can register the callback an go on it's business, and then let the callback do whatever is necessary to be done with the result of the computation. This way you don't tie the threads to sit idly and wait for other threads. Of course I'm just scratching the surface here :)

Cheers,
Rafał

Alberto Adami

unread,
Oct 6, 2014, 10:12:27 AM10/6/14
to scala...@googlegroups.com, johannes...@googlemail.com, ram...@gmail.com, alberto...@gmail.com
I solved using the Action.async in play. Thanks.
Reply all
Reply to author
Forward
0 new messages