Multiple for comprehensions within a map in Scala - scala

Here is the function,
def checkSupport(request: checkSupportRequest): Future[checkSupportResponse] = {
val lookupClient = Thrift.newIface[lookupClient[Future]](/*some initializers*/)
val req = lookupIPRequest(request.getIps, /*Unimportant variables*/)
var result = new checkSupportResponse()
lookupClient.performLookup(req).map { resp =>
// Returns a map of IP to Country Code (String:String)
val ip_cc_map = for {
(ip, lxn) <- resp.found
sr <- lxn.simpleResult
cc <- sr.countryCode
} yield (ip, cc)
// Returns a map of IP to Boolean (String:Boolean)
val not_found_map = for {
(ip) <- resp.notFound
} yield (ip, false)
val op_supp_temp_map = ip_cc_map.map {
case (ip, cc) => someLookup(cc) //This returns a boolean value
}
val op_supp_map = op_supp_temp_map.toList //List of Booleans
val ips_found = ip_cc_map.map { // Intended to become a list of IPs
case (ip, cc) => ip
}
val final_op_supp_map = ips_found zip op_supp_map // Converted to Map (String:Boolean)
final_map = final_op_supp_map ++ not_found_map
result.set_supported(final_result.toList.toMap.mapValues(Boolean.box))
}
Future.value(result)
}
The performLookup is a futurized call. Is it kosher to do things as above i.e. take the output of the futurized call and map it. Perform two back-to-back for comprehensions on it to obtain the necessary maps and then subsequently in the same for, combine the two results and return the same. For some reason, this compiles but when I'm trying to unit-test it, it appears as if the performLookup never ran. Any help would be appreciated.
Also I am in my unit-test doing an Await.result() on the checkSupport() call.

I'm assuming that by Future.value(result) you actually mean Future.successful(result).
The result variable inside the closure is different from the result variable outside of it, so you're never actually modifying the result variable that you're declaring at the beginning of your function. So this is why it looks like your Future never ran.
As a side-note, even if you mutate the state of result, you don't need to declare it as var, val is good here (var would only be necessary if you were reassigning it by doing result = ...).
So you can modify your function like this:
def checkSupport(request: checkSupportRequest): Future[checkSupportResponse] = {
// declare lookupClient and req as before, but not res
lookupClient.performLookup(req).map { resp =>
// do all your processing like before
val result = new checkSupportResponse()
result.setOperator_supported(final_result.toList.toMap.mapValues(Boolean.box))
// We want "result" to be the result of the computation
result
}
}

Related

unable to convert a java.util.List into Scala list

I want that the if block returns Right(List[PracticeQuestionTags]) but I am not able to do so. The if/else returns Either
//I get java.util.List[Result]
val resultList:java.util.List[Result] = transaction.scan(scan);
if(resultList.isEmpty == false){
val listIterator = resultList.listIterator()
val finalList:List[PracticeQuestionTag] = List()
//this returns Unit. How do I make it return List[PracticeQuestionTags]
val answer = while(listIterator.hasNext){
val result = listIterator.next()
val convertedResult:PracticeQuestionTag = rowToModel(result) //rowToModel takes Result and converts it into PracticeQuestionTag
finalList ++ List(convertedResult) //Add to List. I assumed that the while will return List[PracticeQuestionTag] because it is the last statement of the block but the while returns Unit
}
Right(answer) //answer is Unit, The block is returning Right[Nothing,Unit] :(
} else {Left(Error)}
Change the java.util.List list to a Scala List as soon as possible. Then you can handle it in Scala fashion.
import scala.jdk.CollectionConverters._
val resultList = transaction.scan(scan).asScala.toList
Either.cond( resultList.nonEmpty
, resultList.map(rowToModel(_))
, new Error)
Your finalList: List[PracticeQuestionTag] = List() is immutable scala list. So you can not change it, meaning there is no way to add, remove or do change to this list.
One way to achieve this is by using scala functional approach. Another is using a mutable list, then adding to that and that list can be final value of if expression.
Also, a while expression always evaluates to Unit, it will never have any value. You can use while to create your answer and then return it seperately.
val resultList: java.util.List[Result] = transaction.scan(scan)
if (resultList.isEmpty) {
Left(Error)
}
else {
val listIterator = resultList.listIterator()
val listBuffer: scala.collection.mutable.ListBuffer[PracticeQuestionTag] =
scala.collection.mutable.ListBuffer()
while (listIterator.hasNext) {
val result = listIterator.next()
val convertedResult: PracticeQuestionTag = rowToModel(result)
listBuffer.append(convertedResult)
}
Right(listBuffer.toList)
}

How to define a function in scala for flatMap

New to Scala, I want to try to rewrite some code in flatMap by calling a function instead of writing the whole process inside "()".
The original code is like:
val longForm = summary.flatMap(row => {
/*This is the code I want to replace with a function*/
val metric = row.getString(0)
(1 until row.size).map{i=>
(metric,schema(i).name,row.getString(i).toDouble)
})
}/*End of function*/)
The function I wrote is:
def tfunc(line:Row):List[Any] ={
val metric = line.getString(0)
var res = List[Any]
for (i<- 1 to line.size){
/*Save each iteration result as a List[tuple], then append to the res List.*/
val tup = (metric,schema(i).name,line.getString(i).toDouble)
val tempList = List(tup)
res = res :: tempList
}
res
}
The function did not passed compilation with the following error:
error: missing argument list for method apply in object List
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing apply _ or apply(_) instead of apply.
var res = List[Any]
What is wrong with this function?
And for flatMap, is it the write way to return the result as a List?
You haven't explained why you want to replace that code block. Is there a particular goal you're after? There are many, many, different ways that block could be rewritten. How can we know which would be better at meeting you requirements?
Here's one approach.
def tfunc(line :Row) :List[(String,String,Double)] ={
val metric = line.getString(0)
List.tabulate(line.tail.length){ idx =>
(metric, schema(idx+1).name, line.getString(idx+1).toDouble)
}
}

Consolidate a list of Futures into a Map in Scala

I have two case classes P(id: String, ...) and Q(id: String, ...), and two functions returning futures:
One that retrieves a list of objects given a list of id-s:
def retrieve(ids: Seq[String]): Future[Seq[P]] = Future { ... }
The length of the result might be shorter than the input, if not all id-s were found.
One that further transforms P to some other type Q:
def transform(p: P): Future[Q] = Future { ... }
What I would like in the end is, the following. Given ids: Seq[String], calculate a Future[Map[String, Option[Q]]].
Every id from ids should be a key in the map, with id -> Some(q) when it was retrieved successfully (ie. present in the result of retrieve) and also transformed successfully. Otherwise, the map should contain id -> None or Empty.
How can I achieve this?
Is there an .id property on P or Q? You would need one to create the map. Something like this?
for {
ps <- retrieve(ids)
qs <- Future.sequence(ps.map(p => transform(p))
} yield ids.map(id => id -> qs.find(_.id == id)).toMap
Keep in mind that Map[String,Option[X]] is usually not necessary, since if you have Map[String,X] the .get method on the map will give you an Option[X].
Edit: Now assumes that P has a member id that equals the original id-String, otherwise the connection between ids and ps gets lost after retrieve.
def consolidatedMap(ids: Seq[String]): Future[Map[String, Option[Q]]] = {
for {
ps <- retrieve(ids)
qOpts <- Future.traverse(ps){
p => transform(p).map(Option(_)).recover {
// TODO: don't sweep `Throwable` under the
// rug in your real code
case t: Throwable => None
}
}
} yield {
val qMap = (ps.map(_.id) zip qOpts).toMap
ids.map{ id => (id, qMap.getOrElse(id, None)) }.toMap
}
}
Builds an intermediate Map from retrieved Ps and transformed Qs, so that building of ids-to-q-Options map works in linear time.

How to convert a class T object into a Future[T] object in akka

I am trying to do something with akka and scala and i am new to it. I want to append the result of an 'Any' Future into one of the fields in the class
So i have class called T defined as
class T {
var a: String =_
var result = List[Any]= List()
}
Now i have a variable which receives a future value
var futureResult:Future[Any] = //receives a future value
in a function called addResult which takes an T object and returns a future T object.
def addResult(obj1:T):Future[T] ={
var obj2:T = new T()
obj2.a = obj1.a
obj2.result = obj1.result
//I want to append results of futureResult into obj2.result when it is completed
// obj2.result = futureResult :: obj2.result
return Future(obj2)
}
I have to finally call this function from a flow.
val b = Flow[T].mapAsync[T](1)(addResult(_))
First, as #riccardo.cardin noted using T as a name of a class is very bad idea, because of T usual points to generic.
However, you can put any logic in Future with a simple closure, in your case, it looks like this:
Future {
new T("some string", 1 :: 2 :: 3 :: Nil)
}
After that, you can combine asynchronous computation via flatMap, map or with for comprehensive or even use cats black magic with |#|:
for {
f1 <- future1()
f2 <- future2(f1.id)
} yield f2.name
(future1 |#| future2) map { _ - _ }
In your case this transformation depends from logic of your code.
You can change the commented line like this:
futureResult.map { res =>
obj2.result = res :: obj2.result
obj2
}
And then you won't need the last line.

Flatten long nesting of Monadic types in Scala

I have a function to get a seq of workItemIds given JobIds, with following signature -
def getWorkItemIds(jobId: JobId): Future[Seq[WorkItemId]]
I have another function which given a WorkItemId would return a workItem. with signature -
def getWorkItem(workItemId: WorkItemId): Future[Option[WorkItem]]
Now I am trying to write a function which uses these two to return a seq of WorkItem given a JobId, like this -
def getWorkItemsForJob(jobId: JobId): Future[Seq[Future[Option[WorkItem]]]] = {
val workItemIds: Future[Seq[WorkItemId]] = getWorkItemIds(jobId)
val res = workItemIds.map {
idSeq => idSeq.map {
id => getWorkItem(id)
}
}
res
}
The problem is the return type, I don't want to return this monstrous type, instead something simpler like a Future[Seq[WorkItem]] should be returned. Now I can flatten it like -
def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] = {
val workItemIds: Future[Seq[WorkItemId]] = getWorkItemIds(jobId)
val res = workItemIds.map {
idSeq => idSeq.flatMap {
id => getWorkItem(id).get
}
}
res
}
which gives me the correct Future[Seq[WorkItem]] type that I want, but it requires me to do a get on the future, which does not feel correct. I can also use await but that would be a blocking call. Is there anyway to flatten the above type without blocking ?
What you are looking for is Future.traverse:
def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] =
getWorkItemIds(jobId).flatMap(Future.traverse(_)(getWorkItem).map(_.flatten))
The .traverse call takes the Seq from the getWorkItemIds call and returns a Future[Seq] of the results of calling getWorkItem on each entry, without the inner Future wrapping.
The .map(_.flatten) near the end flattens out this inner Seq[Option[WorkItem]] to just Seq[WorkItem].
You can do it the following way:
def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] =
for (
seq <- getWorkItemIds(jobId);
list <- Future.traverse(seq)(getWorkItem)
) yield list.flatten