Composition of Conditional Futures in Scala - scala

I have some code that works for a simple case (2 Futures) but I can't quite find the way to generalize it for an unlimited number of Futures.
What I want to do is create some code that calls a future and, when that future is completed, call another one, and when this one is completed call another one, and so on.
I need the result of each call to be completed before calling the next one because I may not need to call it again (this is my stopping condition).
I know this can be solved explicitly with recursion, but I would like, if at all possible, a solution using for comprehensions and/or folds. I feel there must be a solution like that but I can't write it correctly.
Here's a a function that generates a a list of two random ints
def nextValue: Future[List[Int]] = Future{
Thread.sleep(1000)
val num1 = Random.nextInt(10)
val num2 = Random.nextInt(10)
List(num1,num2)
}
Now I would like to compose infinitely many such futures and join them all at the end (a single future of a list)
I'm just calling await.result for testing purposes
This works for 2 levels, but how to generalize it for N calls?
Await.result({
nextValue.flatMap{ value1 =>
nextValue.map{ value2 =>
value1 ++ value2
}
}
},1.minute)

Future.sequence((0 to 100).map(_ => nextValue)).map(_.flatten)
Usage:
scala> Future.sequence((0 to 100).map(_ => nextValue)).map(_.flatten)
res3: scala.concurrent.Future[scala.collection.immutable.IndexedSeq[Int]] = scala.concurrent.impl.Promise$DefaultPromise#692e028d
scala> Await.result(res3, duration.Duration.Inf)
res4: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 4, 3, 0, 4, 6, 0, 8, 0, 0, 4, 6, 2, 7, 4, 9, 8, 8, 6, 9, 1, 4, 5, 5, 8, 2, 2, 7, 6, 0, 5, 6, 6, 5, 9, 6, 3, 5, 7, 1, 3, 2, 5, 3, 3, 1, 8, 4, 6, 7, 5, 1, 3, 5, 7, 4, 1, 5, 9, 4, 5, 0, 1, 8, 5, 0, 0, 7, 4, 2, 4, 2, 2, 0, 4, 1, 6, 3, 8, 2, 1, 3, 5, 5, 8, 3, 6, 1, 3, 2, 9, 4, 9, 4, 7, 5, 7, 8, 7, 9, 5, 2, 5, 0, 2, 5, 6, 8, 6, 2, 3, 2, 0, 8, 9, 3, 9, 2, 7, 5, 1, 7, 1, 1, 8, 6, 8, 0, 5, 5, 6, 0, 8, 8, 3, 6, 4, 2, 7, 1, 0, 3, 3, 3, 3, 2, 8, 7, 3, 3, 5, 1, 6, 3, 3, 7, 8, 9, 9, 9, 1, 9, 9, 8, 1, 1, 5, 8, 1, 1, 7, 6, 3, 2, 5, 0, 4, 3, 0, 9, 9, 1, 2, 0, 3, 6, 2, 6, 8, 6, 6, 3, 9, 7, 1, 3, 5, 9, 6, 5, 6, 2)
Or with scalaz/cats:
//import scalaz._,Scalaz._
// --or--
//import cats.syntax.traverse._
//import cats.std.list._
//import cats.std.future._
(0 to 100).toList.traverseM(_ => nextValue)
Eplanation from here:
traverseM(f) is equivalent to traverse(f).map(_.join), where join is
the scalaz name for flatten. It's useful as a kind of "lifting
flatMap":
If you want some condition and still need to stay async, you can use fs2:
import fs2._
import fs2.util._
def nextValue: Task[List[Int]] = Task.delay{
import scala.util.Random
val num1 = Random.nextInt(10)
val num2 = Random.nextInt(10)
if(num1 > 5) List(num1,num2) else List()
}
Stream.repeatEval(nextValue).takeWhile(_.size > 0).runLog.map(_.flatten).unsafeRun
https://github.com/functional-streams-for-scala/fs2/blob/series/0.9/docs/guide.md
The same can be achieved with Iteratees:
Cats: https://github.com/travisbrown/iteratee
or scalaz-iteratee package
Generally speaking, you can't implement this with fold because it's actually unfold and there is no good support for unfold in scala as standard library's Stream can't generalize over a Monad/ApplicativeFunctor (like EnumeratorT does) - you can only check a condition by doing Await.result on every unfolding step.

Related

Find the greatest increase value in a Map

I am at the beginning of my Scala journey. I am trying to find and compare the highest increased value of a given dataset - type Map(String, List[Int]). The program should calculate the increase(or decrease) between the 7th last value of the List ant the last value of each row and then print the highest increase row of the entire Map. For example, given the following dataset:
DATASET
SK1, 9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1
SK2, 0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1
SK3, 8, 7, 1, 8, 0, 5, 8, 3, 5, 9, 7, 5, 4, 7, 9, 8, 1, 4, 6, 5, 6, 6, 3, 6, 8, 8, 7, 4, 0, 7
The program should calculate the increase of each row:
SK1 = 1 "last value" - 5 "7th last value" = - 4
SK2 = 1 "last value" - 4 "7th last value" = - 3
SK3 = 7 "last value" - 6 "7th last value" = 1
The program should then print SK3 - 0 because is the highest increase.
The program can calculate the the increase of each row but it currently needs an SK input with the following two methods:
def rise(stock: String): (Int) = {
mapdata.get(stock).map(findLast(_)).getOrElse(0) -
(mapdata.get(stock).map(_.takeRight(7).head.toInt).getOrElse(0))
}
def stockRise(stock: String): (String, Int) = {
(stock, rise(stock))
}
The two methods are then called within the program menu using:
def handleFive(): Boolean = {
menuShowSingleDataStock(stockRise)
true
}
//Pull two rows from the dataset
def menuShowDoubleDataStock(resultCalculator: (String, String) => (String, Int)) = {
print("Please insert the Stock > ")
val stockName1 = readLine
print("Please insert the Stock > ")
val stockName2 = readLine
val result = resultCalculator(stockName1, stockName2)
println(s"${result._1}: ${result._2}")
}
I have tried to call the following method that calculates the rises of every row using the following method but it doesn't seem to be working:
def menuShowStocks(f: () => Map[String, List[Int]]) = {
val highestIncrese = 0
f() foreach { case (x, y) => println(s"$x: $y") }
}
A common approach is:
first map each row, calculate the score
use an aggregation function to select the desired row
Here we go:
scala> val dataSet = Map(
| "SK1" -> List(9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1),
| "SK2" -> List(0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1),
| "SK3" -> List(8, 7, 1, 8, 0, 5, 8, 3, 5, 9, 7, 5, 4, 7, 9, 8, 1, 4, 6, 5, 6, 6, 3, 6, 8, 8, 7, 4, 0, 7)
| )
val dataSet: Map[String, List[Int]] = Map(SK1 -> List(9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1), SK2 -> List(0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1), SK3 -> List(8, 7, 1, 8, 0, 5, 8, 3, 5, 9, 7, 5, 4, 7, 9, 8, 1, 4, 6, 5, 6, 6, 3, 6, 8, 8, 7, 4, 0, 7))
scala> val highestIncrease = dataSet
| .toSeq
| .map { case (name, ints) =>
| name -> (ints.last - ints(ints.length - 7))
| }
| .maxBy(_._2)
val highestIncrease: (String, Int) = (SK3,1)
Some notes:
The map is converted to a Seq first with toSeq. Mapping over Map's is entirely possible but a bit more complicated. Better leave this for a later learning moment. This produces a Seq[(String, List[Int])].
Using map we iterate over the tuples in the Seq. This uses pattern matching to extract the variables name and ints.
The score is calculated. Also, we use the -> operator to construct a new tuple of 2 items so we hang on to the name of the row.
Method maxBy accepts a function to get a value. The expression _._2, equivalent to x => x._2 is a function that gives the second value in each tuple.
The following could print the name of what we found:
println(s"The highest increase is in dataset ${highestIncrease._1} and is ${highestIncrease._2}.")

Searching mapped list to return higher values

My current system can find the last value of a list of integers, shown in the first file below. Then it displays this as the "Current" value. I need to be able to find all of the "Current" values when I enter a search and return all of the results higher than the one I searched for.The list is saved as a Map(String, List[Int]).
SK1, 9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1
SK2, 0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1
SK4, 2, 9, 5, 7, 0, 8, 6, 6, 7, 9, 0, 1, 3, 1, 6, 0, 0, 1, 3, 8, 5, 4, 0, 9, 7, 1, 4, 5, 2, 8
SK5, 2, 6, 8, 0, 3, 5, 5, 2, 5, 9, 4, 5, 3, 5, 7, 8, 8, 2, 5, 9, 3, 8, 6, 7, 8, 7, 4, 1, 2, 3
SK6, 2, 7, 5, 9, 1, 9, 8, 4, 1, 7, 3, 7, 0, 8, 4, 5, 9, 2, 4, 4, 8, 7, 9, 2, 2, 7, 9, 1, 6, 9
SK7, 6, 9, 5, 0, 0, 0, 0, 5, 8, 3, 8, 7, 1, 9, 6, 1, 5, 3, 4, 7, 9, 5, 5, 9, 1, 4, 4, 0, 2, 0
SK8, 2, 8, 8, 3, 1, 1, 0, 8, 5, 9, 0, 3, 1, 6, 8, 7, 9, 6, 7, 7, 0, 9, 5, 2, 5, 0, 2, 1, 8, 6
SK9, 7, 1, 8, 8, 4, 4, 2, 2, 7, 4, 0, 6, 9, 5, 5, 4, 9, 1, 8, 6, 3, 4, 8, 2, 7, 9, 7, 2, 6, 6
This is what I am trying to get; using the data file from above, If I searched for "SK5" the system would return;
Figures higher than SK5 - 3
SK4 - 8
SK6 - 9
SK8 - 6
SK9 - 6
Here is my current code to find the last number in the list
//5 - Show Current Stock Level (W)
def handleFive(): Boolean = {
mnuShowSingleDataStock(currentStockLevel)
true
}
// Returns a single result, not a list
def mnuShowSingleDataStock(stock: (String) => (String,Int)) = {
print("Stock > ")
val data = stock(readLine)
println(s"${data._1}: ${data._2}")
}
//Show higher than stocks
def higherThan(stock: String): List[(String, List[Int])] = {
mapdata.toList.sortWith(_._2.last > _._2.last).takeWhile(row => row._2.last > mapdata.get(stock).map(_.last).getOrElse(0))
}
Sort based on the last value and then take while current stock value is greater than the searched stock value
def higherThan(stock: String): List[(String, List[Int])] = {
mapdata.toList.sortWith(_._2.last > _._2.last).takeWhile(row => row._2.last > mapdata.get(stock).map(_.last).getOrElse(0))
}

Formatting output scala lists

Currently I can search for a value, say SK5 and it will return all values higher than SK5. However the format it is returned in as shown below;
List((SK6,List(2, 7, 5, 9, 1, 9, 8, 4, 1, 7, 3, 7, 0, 8, 4, 5, 9, 2,
4, 4, 8, 7, 9, 2, 2, 7, 9, 1, 6, 9)), (SK4,List(2, 9, 5, 7, 0, 8, 6,
6, 7, 9, 0, 1, 3, 1, 6, 0, 0, 1, 3, 8, 5, 4, 0, 9, 7, 1, 4, 5, 2, 8)),
(SK8,List(2, 8, 8, 3, 1, 1, 0, 8, 5, 9, 0, 3, 1, 6, 8, 7, 9, 6, 7, 7,
0, 9, 5, 2, 5, 0, 2, 1, 8, 6)), (SK9,List(7, 1, 8, 8, 4, 4, 2, 2, 7,
4, 0, 6, 9, 5, 5, 4, 9, 1, 8, 6, 3, 4, 8, 2, 7, 9, 7, 2, 6, 6)),
(SK5,List(2, 6, 8, 0, 3, 5, 5, 2, 5, 9, 4, 5, 3, 5, 7, 8, 8, 2, 5, 9,
3, 8, 6, 7, 8, 7, 4, 1, 2, 3)))
What I want is SK6 - 9, SK4 - 8 etc etc
All bundled together, How would I split this up and only show the last number in the list? I thought I had already filtered this out however apparently not.
Below is my code. Mapdata is saved as Map(String, List[Int])
//functionality to find the last tail element, the "Current" stock price
def findLast(list:List[Int]) = list.last
//8 - Show Stocks Higher Than (W) THIS ONE THIS ONE THIS ONE
def handleEight(): Boolean = {
mnuShowPointsForStockHigher(higherThan2)
true
}
//Returns a list value
def mnuShowPointsForStockHigher(stock: (String) => List[(String, List[Int])]) = {
print("Enter Stock > ")
val data = stock(readLine)
println(s"${data}")
//println(s"${data._1}: ${data._2.last}")
}
def higherThan2(stock: String): List[(String, List[Int])] = {
mapdata.toList.sortWith(_._2.last > _._2.last).takeWhile(row => row._2.last > mapdata.get(stock).map(findLast(_)).getOrElse(0))
}
If you are trying to get the last value in each list, Best option will be map + last. Sorry, don't need to use flatten. My bad .. Should be something line: list.map(x => x.last)

Scala Type Mismatch Mapping

I am trying to create a search function, so the user can search through a list using the key value, however the method Im trying to use returns a type mismatch, Have taken out needless code and shown what is required. How do I set points to take an Int and not "Any"?
"type Any does not conform to type Int"
val mapdata = readFile("data.txt")
def handleTwo(): Boolean = {
mnuShowPointsForTeam(currentPointsForTeam)
true
}
def mnuShowPointsForTeam(f: (String) => (String, Int)) = {
print("Team>")
val data = f(readLine)
println(s"${data._1}: ${data._2}")
}
def currentPointsForTeam(team: String): (String, Int) = {
val points = mapdata.get(team) match{
case Some(p) => p
case None => 0
}
(team, points)
}
The data.txt
SK1, 9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1
SK2, 0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1
SK4, 2, 9, 5, 7, 0, 8, 6, 6, 7, 9, 0, 1, 3, 1, 6, 0, 0, 1, 3, 8, 5, 4, 0, 9, 7, 1, 4, 5, 2, 8
SK5, 2, 6, 8, 0, 3, 5, 5, 2, 5, 9, 4, 5, 3, 5, 7, 8, 8, 2, 5, 9, 3, 8, 6, 7, 8, 7, 4, 1, 2, 3
SK6, 2, 7, 5, 9, 1, 9, 8, 4, 1, 7, 3, 7, 0, 8, 4, 5, 9, 2, 4, 4, 8, 7, 9, 2, 2, 7, 9, 1, 6, 9
SK7, 6, 9, 5, 0, 0, 0, 0, 5, 8, 3, 8, 7, 1, 9, 6, 1, 5, 3, 4, 7, 9, 5, 5, 9, 1, 4, 4, 0, 2, 0
SK8, 2, 8, 8, 3, 1, 1, 0, 8, 5, 9, 0, 3, 1, 6, 8, 7, 9, 6, 7, 7, 0, 9, 5, 2, 5, 0, 2, 1, 8, 6
SK9, 7, 1, 8, 8, 4, 4, 2, 2, 7, 4, 0, 6, 9, 5, 5, 4, 9, 1, 8, 6, 3, 4, 8, 2, 7, 9, 7, 2, 6, 6
It looks like you want to return a tuple with a List[Int], not just a single Int.
If so
def currentPointsForTeam(team: String): (String, List[Int]) =
(team, mapdata.get(team).getOrElse(List.empty))
// Or maybe List(0) instead of List.empty
If you do want to return a single Int, you have to say how to go from the List[Int] in the map to a single value. Maybe a sum?
def currentPointsForTeam(team: String): (String, Int) =
(team, mapdata.get(team).map(_.sum).getOrElse(0))

Creating a list of ints from a txt file

I have an external list in a txt file, I need to grab the first string and use it as a key, thats fine it works, and then I need a list of the numbers afterwards. However I only get the first, what have I done wrong?
So current output would be SK1, 9 - SK2, 0 etc when I need this to be the full list not just the first number.
I am using Scala on Intelije
/**
* Created by Andre on 10/11/2016.
*/
import scala.io.Source
import scala.io.StdIn.readInt
import scala.io.StdIn.readLine
import scala.collection.immutable.ListMap
object StockMarket extends App{
// APPLICATION LOGIC
// reads the data from text file
val mapdata = readFile("data.txt")
// print data to check it's been read in correctly
println(mapdata)
// *******************************************************************************************************************
// UTILITY FUNCTIONS
// reads data file - comma separated file
def readFile(filename: String): Map[String, Int] = {
// create buffer to build up map as we read each line
var mapBuffer: Map[String, Int] = Map()
try {
for (line <- Source.fromFile(filename).getLines()) { // for each line
val splitline = line.split(",").map(_.trim).toList // split line at , and convert to List
// add element to map buffer
// splitline is line from file as List, e.g. List(Bayern Munich, 24)
// use head as key
// tail is a list, but need just the first (only in this case) element, so use head of tail and convert to int
mapBuffer = mapBuffer ++ Map(splitline.head -> splitline.tail.head.toInt)
}
} catch {
case ex: Exception => println("Sorry, an exception happened.")
}
mapBuffer
}
}
My external List
SK1, 9, 7, 2, 0, 7, 3, 7, 9, 1, 2, 8, 1, 9, 6, 5, 3, 2, 2, 7, 2, 8, 5, 4, 5, 1, 6, 5, 2, 4, 1
SK2, 0, 7, 6, 3, 3, 3, 1, 6, 9, 2, 9, 7, 8, 7, 3, 6, 3, 5, 5, 2, 9, 7, 3, 4, 6, 3, 4, 3, 4, 1
SK4, 2, 9, 5, 7, 0, 8, 6, 6, 7, 9, 0, 1, 3, 1, 6, 0, 0, 1, 3, 8, 5, 4, 0, 9, 7, 1, 4, 5, 2, 8
SK5, 2, 6, 8, 0, 3, 5, 5, 2, 5, 9, 4, 5, 3, 5, 7, 8, 8, 2, 5, 9, 3, 8, 6, 7, 8, 7, 4, 1, 2, 3
SK6, 2, 7, 5, 9, 1, 9, 8, 4, 1, 7, 3, 7, 0, 8, 4, 5, 9, 2, 4, 4, 8, 7, 9, 2, 2, 7, 9, 1, 6, 9
SK7, 6, 9, 5, 0, 0, 0, 0, 5, 8, 3, 8, 7, 1, 9, 6, 1, 5, 3, 4, 7, 9, 5, 5, 9, 1, 4, 4, 0, 2, 0
SK8, 2, 8, 8, 3, 1, 1, 0, 8, 5, 9, 0, 3, 1, 6, 8, 7, 9, 6, 7, 7, 0, 9, 5, 2, 5, 0, 2, 1, 8, 6
SK9, 7, 1, 8, 8, 4, 4, 2, 2, 7, 4, 0, 6, 9, 5, 5, 4, 9, 1, 8, 6, 3, 4, 8, 2, 7, 9, 7, 2, 6, 6
Here is your code with minimal changes:
// I split it on two functions just to facilitate testing:
def readFile(filename: String): Map[String, List[Int]] = {
processInput(Source.fromFile(filename).getLines)
}
def processInput(lines: Iterator[String]): Map[String, List[Int]] = {
var mapBuffer: Map[String, List[Int]] = Map()
try {
for (line <- lines) {
val splitline = line.split(",").map(_.trim).toList
// here instead of taking .tail.head, we map over the tail (all numbers):
mapBuffer = mapBuffer + (splitline.head -> splitline.tail.map(_.toInt))
}
} catch {
case ex: Exception => println("Sorry, an exception happened.")
}
mapBuffer
}
And here is a solution, which I believe, is more a idiomatic Scala code:
import scala.util.Try
def processInput(lines: Iterator[String]): Map[String, List[Int]] = {
Try {
lines.foldLeft( Map[String, List[Int]]() ) { (acc, line) =>
val splitline = line.split(",").map(_.trim).toList
acc.updated(splitline.head, splitline.tail.map(_.toInt))
}
}.getOrElse {
println("Sorry, an exception happened.")
Map()
}
}
The differences mainly are
not using var
not using mutable Map (by the way, you don't need a var to mutate
it)
using foldLeft to iterate and accumulate the Map instead of for
using
scala.util.Try
instead of try-catch.