I'm trying to call persistenceService.find() returning a Future[List[Scan]] foreach email passed to my method findByEmails which should return the merged Future[List[Scan]] of every calls to persistenceService.find()
So currently, I have this:
def findByEmails(emails: Set[String]): Future[List[Scan]] = {
val results: Set[Future[List[Scan]]] = emails.map(email => persistenceService.find("report.commitsStats.contributors." + email -> BSONDocument("$exists" -> true)))
}
I can't figure out how to merge each Future[List[Scan]] in the results Set so my method return one Future[List[Scan]].
Any thoughts?
You can use Future.sequence to combine a list of futures, then a flatten on the list should be enough, the code might look similar to this not tested code:
def f(str: String): Future[List[Scan]] = ???
def g(emails: List[String]): Future[List[Scan] = {
val futures: List[Future[list[Scan]]] = emails.map(f)
val futureListList: Future[List[List[Scan]] = Future.sequence(futures)
futureListList.map(_.flatten)
}
Related
I want to to return Future[Map[String, List[String]]] from fetchUniqueCodesForARootCode method
import scala.concurrent._
import ExecutionContext.Implicits.global
case class DiagnosisCode(rootCode: String, uniqueCode: String, description: Option[String] = None)
object Database {
private val data: List[DiagnosisCode] = List(
DiagnosisCode("A00", "A001", Some("Cholera due to Vibrio cholerae")),
DiagnosisCode("A00", "A009", Some("Cholera, unspecified")),
DiagnosisCode("A08", "A080", Some("Rotaviral enteritis")),
DiagnosisCode("A08", "A083", Some("Other viral enteritis"))
)
def getAllUniqueCodes: Future[List[String]] = Future {
Database.data.map(_.uniqueCode)
}
def fetchDiagnosisForUniqueCode(uniqueCode: String): Future[Option[DiagnosisCode]] = Future {
Database.data.find(_.uniqueCode.equalsIgnoreCase(uniqueCode))
}
}
getAllUniqueCodes returns all unique codes from data List.
fetchDiagnosisForUniqueCode returns DiagnosisCode when uniqueCode matches.
From fetchDiagnosisForUniqueCodes, I am returningFuture[List[DiagnosisCode]] using getAllUniqueCodes() and fetchDiagnosisForUniqueCode(uniqueCode).*
def fetchDiagnosisForUniqueCodes: Future[List[DiagnosisCode]] = {
val xa: Future[List[Future[DiagnosisCode]]] = Database.getAllUniqueCodes.map { (xs:
List[String]) =>
xs.map { (uq: String) =>
Database.fetchDiagnosisForUniqueCode(uq)
}
}.map(n =>
n.map(y=>
y.map(_.get)))
}
xa.flatMap {
listOfFuture =>
Future.sequence(listOfFuture)
}}
Now, def fetchUniqueCodesForARootCode should return Future[Map[String, List[DiagnosisCode]]] using fetchDiagnosisForUniqueCodes and groupBy
Here is the method
def fetchUniqueCodesForARootCode: Future[Map[String, List[String]]] = {
fetchDiagnosisForUniqueCodes.map { x =>
x.groupBy(x => (x.rootCode, x.uniqueCode))
}
}
Need to get the below result from fetchUniqueCodesForARootCode:-
A00 -> List(A001, A009), H26 -> List(H26001, H26002), B15 -> List(B150, B159), H26 -> List(H26001, H26002)
It's hard to decode from the question description, what the problem is. But if I understood correctly, you want to get a map from rootCodes to lists of uniqueCodes.
The groupBy method takes a function that for every element returns its key. So first you have to group by the rootCodes and then you have to use map to get the correct values.
groupBy definition: https://dotty.epfl.ch/api/scala/collection/IterableOps.html#groupBy-f68
scastie: https://scastie.scala-lang.org/KacperFKorban/PL1X3joNT3qNOTm6OQ3VUQ
I have been struck with a piece on how to obtain a listbuffer of strings in the case where the listbuffer happens to be constructed in a scala future called in a loop.
Here is a kiss example
def INeedThatListBuffer(): ListBuffer[String] = {
var myCollections: ListBuffer[String] = new ListBuffer[String]()
for (day <- daysInaWeek) {
val myFuture: Future[String] = Future {
// use 'day' do some stuff and get me a result
???
}
myFuture.onComplete {
case Success(result) =>
myCollections += result
}
}
myCollections
}
My problem is that sometimes listBuffer is empty list and sometimes the content that I expected. Clearly, this method is complete before the future is evaluated.
Just to Add
I don't want to used future.await
Passing myCollections as Future obj does not work as there is no binding that myFuture must be complete before myCollections is evaluated.
Kindly help me out.
Thanks
This returns a future. If you don't care about waiting for it to be complete, you can always access the underlying value using .value:
def INeedThatListBuffer(): Future[ListBuffer[String]] = {
def buildFutureFromDay(day: String): Future[String] = Future { ??? }
Future
.sequence(daysInAWeek.map(buildFutureFromDay))
.map(_.foldLeft(ListBuffer[String]())(_ += _))
}
You need to await at some point. Either await for each of the futures to get resolved or change the ListBuffer[String] to ListBuffer[Future[String]] and await on the whole buffer later.
val myFutureCollection: ListBuffer[Future[String]] = new ListBuffer[Future[String]]()
val myFuture: Future[String] = Future(???)
myFutureCollection += myFuture
val eventualBuffer: Future[ListBuffer[String]] = Future.sequence(myFutureCollection)
val buffer: ListBuffer[String] = Await.result(eventualBuffer, 2 seconds)
P.S: You can val instead of var for the list buffer as it is already mutable.
How to implement cache using functional programming
A few days ago I came across callbacks and proxy pattern implementation using scala.
This code should only apply inner function if the value is not in the map.
But every time map is reinitialized and values are gone (which seems obivous.
How to use same cache again and again between different function calls
class Aggregator{
def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
val cache = HashMap[Int, Int]()
(t:Int) => {
if (!cache.contains(t)) {
println("Evaluating..."+t)
val r = function.apply(t);
cache.put(t,r)
r
}
else
{
cache.get(t).get;
}
}
}
def memoizedDoubler = memoize( (key:Int) => {
println("Evaluating...")
key*2
})
}
object Aggregator {
def main( args: Array[String] ) {
val agg = new Aggregator()
agg.memoizedDoubler(2)
agg.memoizedDoubler(2)// It should not evaluate again but does
agg.memoizedDoubler(3)
agg.memoizedDoubler(3)// It should not evaluate again but does
}
I see what you're trying to do here, the reason it's not working is that every time you call memoizedDoubler it's first calling memorize. You need to declare memoizedDoubler as a val instead of def if you want it to only call memoize once.
val memoizedDoubler = memoize( (key:Int) => {
println("Evaluating...")
key*2
})
This answer has a good explanation on the difference between def and val. https://stackoverflow.com/a/12856386/37309
Aren't you declaring a new Map per invocation ?
def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
val cache = HashMap[Int, Int]()
rather than specifying one per instance of Aggregator ?
e.g.
class Aggregator{
private val cache = HashMap[Int, Int]()
def memoize(function: Function[Int, Int] ):Function[Int,Int] = {
To answer your question:
How to implement cache using functional programming
In functional programming there is no concept of mutable state. If you want to change something (like cache), you need to return updated cache instance along with the result and use it for the next call.
Here is modification of your code that follows that approach. function to calculate values and cache is incorporated into Aggregator. When memoize is called, it returns tuple, that contains calculation result (possibly taken from cache) and new Aggregator that should be used for the next call.
class Aggregator(function: Function[Int, Int], cache:Map[Int, Int] = Map.empty) {
def memoize:Int => (Int, Aggregator) = {
t:Int =>
cache.get(t).map {
res =>
(res, Aggregator.this)
}.getOrElse {
val res = function(t)
(res, new Aggregator(function, cache + (t -> res)))
}
}
}
object Aggregator {
def memoizedDoubler = new Aggregator((key:Int) => {
println("Evaluating..." + key)
key*2
})
def main(args: Array[String]) {
val (res, doubler1) = memoizedDoubler.memoize(2)
val (res1, doubler2) = doubler1.memoize(2)
val (res2, doubler3) = doubler2.memoize(3)
val (res3, doubler4) = doubler3.memoize(3)
}
}
This prints:
Evaluating...2
Evaluating...3
I have a uuid generator, like:
class NewUuid {
def apply: String = UUID.randomUUID().toString.replace("-", "")
}
And other class can use it:
class Dialog {
val newUuid = new NewUuid
def newButtons(): Seq[Button] = Seq(new Button(newUuid()), new Button(newUuid()))
}
Now I want to test the Dialog and mock the newUuid:
val dialog = new Dialog {
val newUuid = mock[NewUuid]
newUuid.apply returns "uuid1"
}
dialog.newButtons().map(_.text) === Seq("uuid1", "uuid1")
You can see the returned uuid is always uuid1.
Is it possible to let newUuid to return different values for different calls? e.g. The first call returns uuid1, the second returns uuid2, etc
newUuid.apply returns "uudi1" thenReturns "uuid2"
https://etorreborre.github.io/specs2/guide/SPECS2-3.5/org.specs2.guide.UseMockito.html
Use an iterator to produce your UUIDs
def newUuid() = UUID.randomUUID().toString.replace("-", "")
val defaultUuidSource = Iterator continually newUuid()
class Dialog(uuids: Iterator[String] = defaultUuidSource) {
def newButtons() = Seq(
Button(uuids.next()),
Button(uuids.next())
)
}
Then provide a different one for testing:
val testUuidSource = Iterator from 1 map {"uuid" + _}
new Dialog(testUuidSource)
I have a code block like this:
val await1: List[Int] = await(futureMethod(id))
val mapped = await1.map(entry => {
(pq.id, await(anotherFutureMethod(entry.id)))
})
This fails because of "await must not be used under a nested function" How could I get around this? Why should this be a problem?
I had to guess the signatures of your functions, but an example could look like this:
def futureMethod(id: Int): Future[List[Int]] = Future.successful(0 to id toList)
def anotherFutureMethod(id: Int): Future[String] = Future.successful(id.toString)
def finalFuture(id: Int) = async {
val await1 = await(futureMethod(id))
val mapped = Future.sequence(await1 map anotherFutureMethod)
await(mapped)
}
Using Future.sequence could be a possible, non-blocking solution to avoid using nested await calls.
You'll want to chain the future calls, not block for them. Blocking
futures defies the purpose of a future.
val future1: Future[List[Int]] = futureMethod(id)
val mapped = future1.map(_.flatMap(anotherFutureMethod)
.map(entry => {
(pq.id, entry.id)
}))