Execute Scala Futures in serial one after the other - scala

Given a method that returns a Future like this...
def myMethod(name: String, count: Int, default: Boolean): Future[Unit] = {
...
}
... I need to invoke it N times, so I've defined a list of tuples containing the parameters to be passed:
val paramList = List(
("text1", 22, true),
("text2", 55, true),
("text3", 77, false)
)
How do I invoke myMethod with Future.traverse?
Future.traverse(paramList)(myMethod _).tupled(/* how do I pass the current tuple here? */)

Two possible approaches:
val futuresFromSequence: Future[List[Unit]] = Future.sequence(paramList.map {
case (a,b,c) => myMethod(a,b,c)
})
val futuresFromTraverse: Future[List[Unit]] = Future.traverse(paramList)(x => x match {
case(a,b,c) => myMethod(a,b,c)
})
Note that traverse takes some collection and a function from an item of that collection to a future, sequence instead takes a list of futures and folds it to a future of list.
If you want to stick with the tupled syntax (which I personally dislike):
Future.traverse(paramList)(x => (myMethod _).tupled(x))
As noted in the comments you may want to run them in serial (although it's not 100% clear from the question), in that case you can use foldLeft and flatMap to chain future execution:
myList.foldLeft(Future(List.empty[Unit]))((prevFuture, currentTuple) => {
for {
prev <- prevFuture
curr <- (myMethod _).tupled(currentTuple)
} yield prev :+ curr
})
Where basically the first generator waits for the future in the accumulator to complete before launching the new one, this also returns a Future[List[Unit]].

Related

functional scala- how to avoid deep nesting on optional mappings

I have a set of operations that are completed in sequence, but if an intermediate sequence returns "null" I would like to abort the operation early (skip the subsequent steps).
I conjured up a function like this which given an input parameter, performs several operations against Redis and will return a product if it exists. Since it is possible that one of the intermediate requests returns a null value, the complete operation could "fail" and I would like to short circuit the unnecessary steps that come afterwards.
The nesting here is becoming crazy, and I would like to make it more legible. Is there a proper "functional" way to perform this type of "if/else" short circuiting?
def getSingleProduct(firstSku: String): Option[Product] = {
val jedis = pool.getResource
val sid: Array[Byte] = jedis.get(Keys.sidForSku(firstSku, sectionId, feedId).getBytes)
Option(sid).flatMap {
sid: Array[Byte] =>
Option(jedis.get(Keys.latestVersionForSid(sectionId, feedId, sid))) flatMap {
version: Array[Byte] =>
Option(Keys.dataForSid(sectionId, feedId, version, sid)) flatMap {
getDataKey: Array[Byte] =>
Option(jedis.get(getDataKey)) flatMap {
packedData: Array[Byte] =>
val data = doSomeStuffWith(packedData)
Option(Product(data, "more interesting things"))
}
}
}
}
}
The way to do this is to use for:
for {
sid <- Option(jedis.get(...))
version <- Option(jedis.get(..., sid, ...))
getDataKey <- Option(jedis.get(...version,...))
packedData <- Option(jedis.get(getDataKey))
} yield {
// Do stuff with packedData
}
This will return None if any of the get calls returns None, otherwise it will return Some(x) where x is the result of the yeild expression.
You might also want to consider writing a wrapper for jedis.get which returns Option(x) rather than using null as the error result.

Scala: Use map function tuples in a subsequent flatMap

I want to use a tuple of a map function in a subsequent flatMap.
Like this:
val list = orders.map(ord => (ord.prod.tasks, ord.quantity))
.zipWithIndex flatMap {
case (variable, index) =>
createSchedules(variable._1.size * variable._2, ord, null)
}
Is there a way in which I can use it or do you think that I have to change the way that I'm thinking about the solution?
I want to use a tuple of a map function in a subsequent flatMap.
Here's a working example of using tuples in a subsequent flatMap
val input = List("a", "b", "c")
val list = input.map(i => (i + "A", i + "B")).zipWithIndex flatMap{ case (variable, index)=> variable._1 + variable._2}
Issue with original code
val list = orders.map(ord => (ord.prod.tasks, ord.quantity)).zipWithIndex flatMap{case (variable, index)=>createSchedules(variable._1.size * variable._2, ord, null)}
One issue is that ord is out of scope in the 'createSchedules' call.
ord will only be visible in the initial 'orders.map' scope.
First of all, judging from the parameters that you're passing to createSchedules, it looks as if that function can be simplified to (I'm ignoring the null parameter for now):
def createSchedules(order: Order): List[Schedule] = {
val num = order.prod.tasks.size * order.quantity
// do stuff
}
Also, the initial map is unnecessary. list can be simplified to:
val list = orders.zipWithIndex
.flatMap {
case (order, index) =>
createSchedules(order)
}
It's unclear what you need the index for, though.

Working scala code using a var in a pure function. Is this possible without a var?

Is it possible (or even worthwhile) to try to write the below code block without a var? It works with a var. This is not for an interview, it's my first attempt at scala (came from java).
The problem: Fit people as close to the front of a theatre as possible, while keeping each request (eg. Jones, 4 tickets) in a single theatre section. The theatre sections, starting at the front, are sized 6, 6, 3, 5, 5... and so on. I'm trying to accomplish this by putting together all of the potential groups of ticket requests, and then choosing the best fitting group per section.
Here are the classes. A SeatingCombination is one possible combination of SeatingRequest (just the IDs) and the sum of their ticketCount(s):
class SeatingCombination(val idList: List[Int], val seatCount: Int){}
class SeatingRequest(val id: Int, val partyName: String, val ticketCount: Int){}
class TheatreSection(val sectionSize: Int, rowNumber: Int, sectionNumber: Int) {
def id: String = rowNumber.toString + "_"+ sectionNumber.toString;
}
By the time we get to the below function...
1.) all of the possible combinations of SeatingRequest are in a list of SeatingCombination and ordered by descending size.
2.) all of the TheatreSection are listed in order.
def getSeatingMap(groups: List[SeatingCombination], sections: List[TheatreSection]): HashMap[Int, TheatreSection] = {
var seatedMap = new HashMap[Int, TheatreSection]
for (sect <- sections) {
val bestFitOpt = groups.find(g => { g.seatCount <= sect.sectionSize && !isAnyListIdInMap(seatedMap, g.idList) })
bestFitOpt.filter(_.idList.size > 0).foreach(_.idList.foreach(seatedMap.update(_, sect)))
}
seatedMap
}
def isAnyListIdInMap(map: HashMap[Int, TheatreSection], list: List[Int]): Boolean = {
(for (id <- list) yield !map.get(id).isEmpty).reduce(_ || _)
}
I wrote the rest of the program without a var, but in this iterative section it seems impossible. Maybe with my implementation strategy it's impossible. From what else I've read, a var in a pure function is still functional. But it's been bothering me I can't think of how to remove the var, because my textbook told me to try to avoid them, and I don't know what I don't know.
You can use foldLeft to iterate on sections with a running state (and again, inside, on your state to add iteratively all the ids in a section):
sections.foldLeft(Map.empty[Int, TheatreSection]){
case (seatedMap, sect) =>
val bestFitOpt = groups.find(g => g.seatCount <= sect.sectionSize && !isAnyListIdInMap(seatedMap, g.idList))
bestFitOpt.
filter(_.idList.size > 0).toList. //convert option to list
flatMap(_.idList). // flatten list from option and idList
foldLeft(seatedMap)(_ + (_ -> sect))) // add all ids to the map with sect as value
}
By the way, you can simplify the second method using exists and map.contains:
def isAnyListIdInMap(map: HashMap[Int, TheatreSection], list: List[Int]): Boolean = {
list.exists(id => map.contains(id))
}
list.exists(predicate: Int => Boolean) is a Boolean which is true if the predicate is true for any element in list.
map.contains(key) checks if map is defined at key.
If you want to be even more concise, you don't need to give a name to the argument of the predicate:
list.exists(map.contains)
Simply changing var to val should do it :)
I think, you may be asking about getting rid of the mutable map, not of the var (it doesn't need to be var in your code).
Things like this are usually written recursively in scala or using foldLeft, like other answers suggest. Here is a recursive version:
#tailrec
def getSeatingMap(
groups: List[SeatingCombination],
sections: List[TheatreSection],
result: Map[Int, TheatreSection] = Map.empty): Map[Int, TheatreSection] = sections match {
case Nil => result
case head :: tail =>
val seated = groups
.iterator
.filter(_.idList.nonEmpty)
.filterNot(_.idList.find(result.contains).isDefined)
.find(_.seatCount <= head.sectionSize)
.fold(Nil)(_.idList.map(id => id -> sect))
getSeatingMap(groups, tail, result ++ seated)
}
btw, I don't think you need to test every id in list for presence in the map - should suffice to just look at the first one. You could also make it a bit more efficient, probably, if instead of checking the map every time to see if the group is already seated, you'd just drop it from the input list as soon as the section is assigned.
#tailrec
def selectGroup(
sect: TheatreSection,
groups: List[SeatingCombination],
result: List[SeatingCombination] = Nil
): (List[(Int, TheatreSection)], List[SeatingCombination]) = groups match {
case Nil => (Nil, result)
case head :: tail
if(head.idList.nonEmpty && head.seatCount <= sect.sectionSize) => (head.idList.map(_ -> sect), result.reverse ++ tail)
case head :: tail => selectGroup(sect, tail, head :: result)
}
and then in getSeatingMap:
...
case head :: tail =>
val(seated, remaining) => selectGroup(sect, groups)
getSeatingMap(remaining, tail, result ++ seated)
Here is how I was able to achieve without using the mutable.HashMap, the suggestion by the comment to use foldLeft was used to do it:
class SeatingCombination(val idList: List[Int], val seatCount: Int){}
class SeatingRequest(val id: Int, val partyName: String, val ticketCount: Int){}
class TheatreSection(val sectionSize: Int, rowNumber: Int, sectionNumber: Int) {
def id: String = rowNumber.toString + "_"+ sectionNumber.toString;
}
def getSeatingMap(groups: List[SeatingCombination], sections: List[TheatreSection]): Map[Int, TheatreSection] = {
sections.foldLeft(Map.empty[Int, TheatreSection]) { (m, sect) =>
val bestFitOpt = groups.find(g => {
g.seatCount <= sect.sectionSize && !isAnyListIdInMap(m, g.idList)
}).filter(_.idList.nonEmpty)
val newEntries = bestFitOpt.map(_.idList.map(_ -> sect)).getOrElse(List.empty)
m ++ newEntries
}
}
def isAnyListIdInMap(map: Map[Int, TheatreSection], list: List[Int]): Boolean = {
(for (id <- list) yield map.get(id).isDefined).reduce(_ || _)
}

Dynamically create for comprehension of Futures and wait for completion

I have the following code:
// Start async functions
val async1: Future[Seq[Int]] = ...
val async2: Future[Seq[Int]] = ...
val async3: Future[Seq[Int]] = ...
// Wait for completion
(for {
a1 <- async1
a2 <- async2
a3 <- async3
} yield (a1, a2, a3)).map {
// Use the results
}
I want to improve this to handle a variable amount of async functions (and not necessarily calling each of them every time). What I have done so far is:
// Start the async functions ?
val asyncs: Seq[Future[Seq[Int]] = otherList.filter(x => someCondition).map(x => asyncFunc(x))
// Wait for the functions to finish ?
(for (seqInt <- asyncs) yield seqInt).map {
case results => // <-- problem here
// Use the results
}
The problem I am having is that the results are of type Future[Seq[Int]], but I expected they would be of type (Seq[Int], Seq[Int], Seq[Int]) like in the first snippet.
In the end I would like to do is kickoff a dynamic amount of async functions which all have the same Future return type, wait for them all to finish, then use all of their results together.
Future.sequence is the key part I was missing (thanks for the comment)
// Create a list of Futures
val asyncs: Seq[Future[Seq[Int]] = otherList.filter(x => someCondition).map(x => asyncFunc(x))
// Use Future.sequence to to execute them and return a list of sequence of integers
Future.sequence(asyncs).map{
case results => // Use the results List[Seq[Int]]
}.recover {
case error => // Oh no!
}

How to get a result from Enumerator/Iteratee?

I am using play2 and reactivemongo to fetch a result from mongodb. Each item of the result needs to be transformed to add some metadata. Afterwards I need to apply some sorting to it.
To deal with the transformation step I use enumerate():
def ideasEnumerator = collection.find(query)
.options(QueryOpts(skipN = page))
.sort(Json.obj(sortField -> -1))
.cursor[Idea]
.enumerate()
Then I create an Iteratee as follows:
val processIdeas: Iteratee[Idea, Unit] =
Iteratee.foreach[Idea] { idea =>
resolveCrossLinks(idea) flatMap { idea =>
addMetaInfo(idea.copy(history = None))
}
}
Finally I feed the Iteratee:
ideasEnumerator(processIdeas)
And now I'm stuck. Every example I saw does some println inside foreach, but seems not to care about a final result.
So when all documents are returned and transformed how do I get a Sequence, a List or some other datatype I can further deal with?
Change the signature of your Iteratee from Iteratee[Idea, Unit] to Iteratee[Idea, Seq[A]] where A is the type. Basically the first param of Iteratee is Input type and second param is Output type. In your case you gave the Output type as Unit.
Take a look at the below code. It may not compile but it gives you the basic usage.
ideasEnumerator.run(
Iteratee.fold(List.empty[MyObject]) { (accumulator, next) =>
accumulator + resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
}
}
) // returns Future[List[MyObject]]
As you can see, Iteratee is a simply a state machine. Just extract that Iteratee part and assign it to a val:
val iteratee = Iteratee.fold(List.empty[MyObject]) { (accumulator, next) =>
accumulator + resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
}
}
and feel free to use it where ever you need to convert from your Idea to List[MyObject]
With the help of your answers I ended up with
val processIdeas: Iteratee[Idea, Future[Vector[Idea]]] =
Iteratee.fold(Future(Vector.empty[Idea])) { (accumulator: Future[Vector[Idea]], next:Idea) =>
resolveCrossLinks(next) flatMap { next =>
addMetaInfo(next.copy(history = None))
} flatMap (ideaWithMeta => accumulator map (acc => acc :+ ideaWithMeta))
}
val ideas = collection.find(query)
.options(QueryOpts(page, perPage))
.sort(Json.obj(sortField -> -1))
.cursor[Idea]
.enumerate(perPage).run(processIdeas)
This later needs a ideas.flatMap(identity) to remove the returning Future of Futures but I'm fine with it and everything looks idiomatic and elegant I think.
The performance gained compared to creating a list and iterate over it afterwards is negligible though.