In the following example, items with different discriminators ("a", "b" and "c") are evaluated (printed) in parallel:
package org.example
import cats.effect.std.Random
import cats.effect.{ExitCode, IO, IOApp, Temporal}
import cats.syntax.all._
import cats.{Applicative, Monad}
import fs2._
import scala.concurrent.duration._
object GitterQuestion extends IOApp {
override def run(args: List[String]): IO[ExitCode] =
Random.scalaUtilRandom[IO].flatMap { implicit random =>
val flat = Stream(
("a", 1),
("a", 2),
("a", 3),
("b", 1),
("b", 2),
("b", 3),
("c", 1),
("c", 2),
("c", 3)
).covary[IO]
val a = flat.filter(_._1 === "a").through(rndDelay)
val b = flat.filter(_._1 === "b").through(rndDelay)
val c = flat.filter(_._1 === "c").through(rndDelay)
val nested = Stream(a, b, c)
nested.parJoin(100).printlns.compile.drain.as(ExitCode.Success)
}
def rndDelay[F[_]: Monad: Random: Temporal, A]: Pipe[F, A, A] =
in =>
in.evalMap { v =>
(Random[F].nextDouble.map(_.seconds) >>= Temporal[F].sleep) >> Applicative[F].pure(v)
}
}
The result of running this program will look similar to this:
(c,1)
(a,1)
(c,2)
(a,2)
(c,3)
(b,1)
(a,3)
(b,2)
(b,3)
Note that there's no reordering between items with the same discriminator - they are processed sequentially. (a, 2) will never be printed before (a, 1).
In my real-world scenario, the discriminator values are not known ahead of time and there can be many of them, but I would like to have the same behavior, how can I do this?
I think you need to roll your own groupBy function for this. I think you would have to create a Queue for every discriminator. Then for every Queue emit one inner Stream that pulls elements from that Queue.
Here's an untested and probably naive implementation of what I had in mind:
import cats.effect.std.Queue
val nested =
(flat.map(Some(_)) ++ Stream(None))
.evalScan(Map.empty[String, Queue[IO, Option[(String, Int)]]] -> Option.empty[Stream[IO, (String, Int)]]){
case ((map, _), t # Some((key, value))) =>
if (map.contains(key))
map(key).offer(t).as(map -> None)
else {
for {
q <- Queue.unbounded[IO, Option[(String, Int)]]
_ <- q.offer(t)
r = (map + (key -> q)) -> Some(Stream.fromQueueNoneTerminated(q))
} yield r
}
case ((map, _), None) =>
// None means the flat stream is finished
map.values.toList.traverse(_.offer(None))
.as(Map.empty -> None)
}
.map(_._2).unNone
val parallelism: Int = ???
nested
.map(_.through(rndDelay))
// produce and consume in parallel in order to
// avoid deadlocks in case of bounded parJoin
.prefetchN(parallelism)
.parJoin(parallelism)
.printlns
.compile
.drain
.as(ExitCode.Success)
I believe that broadcastThrough does what you want.
(but make sure to check the Scaladoc carefully)
I am using IO directly for simplicity but it should be easy to adapt to abstract F[_]
def discriminateProcessing[A, B](stream: Stream[IO, A])(discriminators: List[A => Boolean])(pipe: Pipe[IO, A, B]): Stream[IO, B] = {
val allPipes: List[Pipe[IO, A, B]] = discriminators.map { p =>
s => s.filter(p).through(pipe)
}
stream.broadcastThrough(allPipes : _*)
}
Which would be used like this:
val result = discriminateProcessing(stream = flat)(discriminators = List(
_._1 === "a",
_._1 === "b",
_._1 === "c"
)) { s =>
s.evalMap { v =>
random.nextDouble.map(_.seconds).flatMap(IO.sleep).as(v)
}
}
You can see the code running here.
Related
Hello I have this exercise where I am trying to find all numbers that do not exist in the second element of a tuple, and print them out in a list. I have a working version but it returns duplicate values, and it is not being returned in a list. I cant seem to use distinct here. Can any one explain to me why distinct cannot be used here and what i would need to do instead. Also if there is a better way to get the answer to the exercise, that would be appreciated.
object Example{
def main(args: Array[String]): Unit = {
val tupleExercise: List[(Int,Int)] = List(
(1, 3),
(2, 3),
(3, 6),
(5, 6),
(5, 7),
(4, 5),
(4, 8),
(4, 9),
(9, 11)
)
def notExistsInSecond(n: List[(Int, Int)]): Unit = {
var potential =
for (a <- n) {
for (c <- n)
if(c._1 == a._2) println(a._1)
}
}
println(notExistsInSecond(tupleExercise))
}
}
//expected ouput
// [1, 2, 4]
I don't think there is a (performant) way to write it in one line, but you could write it like this:
def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
val second = n.map(_._2).distinct
n.map(_._1).distinct.filterNot(second.contains)
}
It gets probabely more performant if you use a HashSet instead of a List for the lookup:
def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
val valuesSet = n.map(_._2).toSet
n.map(_._1).distinct.filterNot(valuesSet.contains)
}
Here's a way to do it with a single pass through the input list and using Set to insure distinct results.
def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
val (s1,s2) = n.foldLeft((Set.empty[Int],Set.empty[Int])){
case ((sa,sb),(a,b)) => (sa+a, sb+b)
}
(s1 diff s2).toList
}
The same logic as Raphael but more performant.
def notExistsInSecond[A](n: List[(A, A)]): List[A] = {
val seconds = n.iterator.map(_._2).toSet
val (result, _) = n.foldLeft(List.empty[Int] -> Set.empty[Int]) {
case ((result, added), (e, _)) =>
if (!seconds(e) && !added(e))
(e :: result) -> (added + e)
else
result -> added
}
result.reverse // If you do not care about the order you can remove this reverse
}
Or if you do not care about the result being a list, this would be even faster:
def notExistsInSecond[A](n: List[(A, A)]): Set[A] = {
val seconds = n.iterator.map(_._2).toSet
n.iterator.collect {
case (e, _) if (!seconds(e)) => e
}.toSet
}
I know we should be cautious when removing an entry while traversing a collection in many languages like C++ or Java, but not sure if Scala makes any difference.
So is below code snippet safe?
val mutableMap: [String, List[String]] = ...
mutableMap.foreach { case (k, list) =>
// do sth. to list
if (list.isEmtpy) mutableMap -= k
}
What's the recommended way if I want to update the value for a (k -> v) mapping?
val mutableMap: [String, List[String]] = ...
mutableMap.foreach { case (k, list) =>
mutableMap += (k, anotherList) // is this safe?
}
Thanks!
I guess you can achieve same with an immutable map like below;
val map: Map[String, List[Int]] = Map(
"apple" -> List(),
"orange" -> List(1),
"banana" -> List(1, 2),
"pineapple" -> List(1, 2, 3),
"kiwi" -> List(1, 2, 3, 4)
)
def doSomething(list: List[Int]) = ()
map.foldLeft(map) {
case (acc, (key, value)) =>
doSomething(value)
if (value.isEmpty) acc - key else acc
}
but I expect doSomething method is probably doing something that would count as a side effect. If you can afford using the cats library i would suggest you to do it in a way like below;
import cats.effect.IO
import cats.implicits._
def doSomething(list: List[Int]): IO[Option[List[Int]]] = IO.pure(Some(list).filter(_.nonEmpty))
val program = map.toList
.traverseFilter { case (key, value) => doSomething(value).map(_.map(key -> _))}
.map(_.toMap)
program.unsafeRunSync()
You should consider using the inbuilt method filterInPlace to remove the entries while treating.
val mutableMap: [String, List[String]] = ...
mutableMap.filterInPlace((k, list) => (list.nonEmpty))
Suppose I've got a function fab: A => Future[B]. Now I need to write new function foo to process Seq[A] and accumulate all errors. That's why I cannot use Future.traverse because it "fails fast" and doesn't accumulate the errors.
foo receives Seq[A] and should return a Future. The client should get either B or an exception for each element of the input Seq[A]. What would be a signature of this function ?
To define foo for what you need, consider using Future.sequence on top of map/recover after applying fab to individual elements of the input list, as shown below:
import scala.concurrent.{ Future, ExecutionContext }
def foo[A, B](ls: List[A])(fab: A => Future[B])(implicit ec: ExecutionContext):
Future[List[Either[Throwable, B]]] =
Future.sequence(ls.map(fab).map(_.map(Right(_)).recover{ case e => Left(e) }))
Note that instead of Seq, immutable List is preferred hence is being used here. Change it to Seq if necessary.
Testing foo:
implicit val ec = ExecutionContext.global
def fab(s: String): Future[Int] = Future{ 10 / s.length }
val ls = List("abcd", "", "xx", "")
foo(ls)(fab)
// res1: Future[List[Either[Throwable, Int]]] = Future(Success(List(
// Right(2),
// Left(java.lang.ArithmeticException: / by zero),
// Right(5),
// Left(java.lang.ArithmeticException: / by zero)
// )))
I have a solution with ZIO.
I added this fake function:
def fab(implicit ec: ExecutionContext): Int => Future[String] = i => Future(
if (i % 3 == 0)
throw new IllegalArgumentException(s"bad $i")
else
s"$i"
)
Now we create a Stream of Int's and run fab for each of them
val stream =
ZStream.fromIterable(Seq(1, 2, 3, 4, 5))
.map(in => Task.fromFuture(implicit ec => fab(ec)(in)))
val sink = Sink.collectAll[Task[String]]
Now we collect the successes and failures:
val collect: ZIO[zio.ZEnv, Throwable, (List[String], List[Throwable])] = for {
strs <- stream.run(sink)
successes <- Task.collectAllSuccesses(strs)
failures <- ZIO.collectAllSuccesses(strs.map(_.flip))
} yield (successes, failures)
Running and printing this:
new DefaultRuntime {}
.unsafeRun(
collect
.tapError { ex => zio.console.putStrLn(s"There was an exception: ${ex.getMessage}") }
.tap { case (successes, failures) => zio.console.putStrLn(s"($successes, $failures)") }
.fold(_ => -1, _ => 0)
)
Prints us:
(List(1, 2, 4, 5), List(java.lang.IllegalArgumentException: bad 3))
Let me know if you are need more explaining - if ZIO is an option.
I am currently working on a function that takes in a Map[String, List[String]] and a String as arguments. The map contains a user Id and the IDs of films that they liked. What I need to do is, to return a List[List[String]] which contains the other movies that where liked by the user who liked the movie that was passed into the function.
The function declaration looks as follows:
def movies(m: Map[String, List[String]], mov: String) : List[List[String]]= {
}
So lets imagine the following:
val m1 : [Map[Int, List[String]]] = Map(1 ‐> List("b", "a"), 2 ‐> List("y", "x"), 3 ‐> List("c", "a"))
val movieID = "a"
movies(m1, movieId)
This should return:
List(List("b"), List("c"))
I have tried using
m1.filter(x => x._2.contains(movieID))
So that only Lists containing movieID are kept in the map, but my problem is that I need to remove movieID from every list it occurs in, and then return the result as a List[List[String]].
You could use collect:
val m = Map("1" -> List("b", "a"), "2" -> List("y", "x"), "3" -> List("c", "a"))
def movies(m: Map[String, List[String]], mov: String) = m.collect {
case (_, l) if l.contains(mov) => l.filterNot(_ == mov)
}
movies(m, "a") //List(List(b), List(c))
Problem with this approach is, that it would iterate over every movie list twice, the first time with contains and the second time with filterNot. We could optimize it tail-recursive function, which would look for element and if found just return list without it:
import scala.annotation.tailrec
def movies(m: Map[String, List[String]], mov: String) = {
#tailrec
def withoutElement[T](l: List[T], mov: T, acc: List[T] = Nil): Option[List[T]] = {
l match {
case x :: xs if x == mov => Some(acc.reverse ++ xs)
case x :: xs => withoutElement(xs, mov, x :: acc)
case Nil => None
}
}
m.values.flatMap(withoutElement(_, mov))
}
The solution from Krzysztof is a good one. Here's an alternate way to traverse every List just once.
def movies(m: Map[String, List[String]], mov: String) =
m.values.toList.flatMap{ss =>
val tpl = ss.foldLeft((false, List.empty[String])){
case ((_,res), `mov`) => (true, res)
case ((keep,res), str) => (keep, str::res)
}
if (tpl._1) Some(tpl._2) else None
}
This should work for you:
object DemoAbc extends App {
val m1 = Map(1 -> List("b", "a"), 2 -> List("y", "x"), 3 -> List("c", "a"))
val movieID = "a"
def movies(m: Map[Int, List[String]], mov: String): List[List[String]] = {
val ans = m.foldLeft(List.empty[List[String]])((a: List[List[String]], b: (Int, List[String])) => {
if (b._2.contains(mov))
b._2.filter(_ != mov) :: a
else a
})
ans
}
print(movies(m1, movieID))
}
Is there a simple way to flatten a collection of try's to give either a success of the try values, or just the failure?
For example:
def map(l:List[Int]) = l map {
case 4 => Failure(new Exception("failed"))
case i => Success(i)
}
val l1 = List(1,2,3,4,5,6)
val result1 = something(map(l1))
result1: Failure(Exception("failed"))
val l2 = List(1,2,3,5,6)
val result2 = something(map(l2))
result2: Try(List(1,2,3,5,6))
And can how would you handle multiple Failures in the collection?
This is pretty close to minimal for fail-first operation:
def something[A](xs: Seq[Try[A]]) =
Try(xs.map(_.get))
(to the point where you shouldn't bother creating a method; just use Try). If you want all the failures, a method is reasonable; I'd use an Either:
def something[A](xs: Seq[Try[A]]) =
Try(Right(xs.map(_.get))).
getOrElse(Left(xs.collect{ case Failure(t) => t }))
A little less verbose, and more type safe:
def sequence[T](xs : Seq[Try[T]]) : Try[Seq[T]] = (Try(Seq[T]()) /: xs) {
(a, b) => a flatMap (c => b map (d => c :+ d))
}
Results:
sequence(l1)
res8: scala.util.Try[Seq[Int]] = Failure(java.lang.Exception: failed)
sequence(l2)
res9: scala.util.Try[Seq[Int]] = Success(List(1, 2, 3, 5, 6))
Maybe not as simple as you hoped for, but this works:
def flatten[T](xs: Seq[Try[T]]): Try[Seq[T]] = {
val (ss: Seq[Success[T]]#unchecked, fs: Seq[Failure[T]]#unchecked) =
xs.partition(_.isSuccess)
if (fs.isEmpty) Success(ss map (_.get))
else Failure[Seq[T]](fs(0).exception) // Only keep the first failure
}
val xs = List(1,2,3,4,5,6)
val ys = List(1,2,3,5,6)
println(flatten(map(xs))) // Failure(java.lang.Exception: failed)
println(flatten(map(ys))) // Success(List(1, 2, 3, 5, 6))
Note that the use of partition is not as type safe as it gets, as witnessed by the #unchecked annotations. In that respect, a foldLeft that accumulates two sequences Seq[Success[T]] and Seq[Failure[T]] would be better.
If you wanted to keep all failures, you can use this:
def flatten2[T](xs: Seq[Try[T]]): Either[Seq[T], Seq[Throwable]] = {
val (ss: Seq[Success[T]]#unchecked, fs: Seq[Failure[T]]#unchecked) =
xs.partition(_.isSuccess)
if (fs.isEmpty) Left(ss map (_.get))
else Right(fs map (_.exception))
}
val zs = List(1,4,2,3,4,5,6)
println(flatten2(map(xs))) // Right(List(java.lang.Exception: failed))
println(flatten2(map(ys))) // Left(List(1, 2, 3, 5, 6))
println(flatten2(map(zs))) // Right(List(java.lang.Exception: failed,
// java.lang.Exception: failed))
As an addition to Impredicative's answer and comment, if you have both scalaz-seven and scalaz-contrib/scala210 in your dependencies:
> scala210/console
[warn] Credentials file /home/folone/.ivy2/.credentials does not exist
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.0 (OpenJDK 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.util._
import scala.util._
scala> def map(l:List[Int]): List[Try[Int]] = l map {
| case 4 => Failure(new Exception("failed"))
| case i => Success(i)
| }
map: (l: List[Int])List[scala.util.Try[Int]]
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> import scalaz.contrib.std.utilTry._
import scalaz.contrib.std.utilTry._
scala> val l1 = List(1,2,3,4,5,6)
l1: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> map(l1).sequence
res2: scala.util.Try[List[Int]] = Failure(java.lang.Exception: failed)
scala> val l2 = List(1,2,3,5,6)
l2: List[Int] = List(1, 2, 3, 5, 6)
scala> map(l2).sequence
res3: scala.util.Try[List[Int]] = Success(List(1, 2, 3, 5, 6))
You need scalaz to get an Applicative instance for the List (hidden in the MonadPlus instance), to get the sequence method. You need scalaz-contrib for the Traverse instance of Try, which is required by the sequence's type signature.
Try lives outside of scalaz, since it only appeared in scala 2.10, and scalaz aims to cross-compile to earlier versions).
Starting in Scala 2.13, most collections are provided with a partitionMap method which partitions elements based on a function returning either Right or Left.
In our case we can call partitionMap with a function that transforms our Trys into Eithers (Try::toEither) in order to partition Successes as Rights and Failures as Lefts.
Then it's just a matter of matching the resulting partitioned tuple of lefts and rights based on whether or not there are lefts:
tries.partitionMap(_.toEither) match {
case (Nil, rights) => Success(rights)
case (firstLeft :: _, _) => Failure(firstLeft)
}
// * val tries = List(Success(10), Success(20), Success(30))
// => Try[List[Int]] = Success(List(10, 20, 30))
// * val tries = List(Success(10), Success(20), Failure(new Exception("error1")))
// => Try[List[Int]] = Failure(java.lang.Exception: error1)
Details of the intermediate partitionMap step:
List(Success(10), Success(20), Failure(new Exception("error1"))).partitionMap(_.toEither)
// => (List[Throwable], List[Int]) = (List(java.lang.Exception: error1), List(10, 20))
Have a look on the liftweb Box monad. With the help of the tryo constructor function, it gives you exactly the abstraction you are looking for.
With tryo you can lift a function into a Box. The box then either contains the result from the function or it contains an error. You can then access the box with the usual monadic helper functions (flatMap, filter, etc.), without bothering if the box contains an error or the result form the function.
Example:
import net.liftweb.util.Helpers.tryo
List("1", "2", "not_a_number") map (x => tryo(x.toInt)) map (_ map (_ + 1 ))
Results to
List[net.liftweb.common.Box[Int]] =
List(
Full(2),
Full(3),
Failure(For input string: "not_a_number",Full(java.lang.NumberFormatException: For input string: "not_a_number"),Empty)
)
You can skip the erroneous values with flatMap
List("1", "2", "not_a_number") map (x => tryo(x.toInt)) flatMap (_ map (_ + 1 ))
Results
List[Int] = List(2, 3)
There are multiple other helper methods, e.g. for combining boxes (while chaining error messages). You can find a good overview here: Box Cheat Sheet for Lift
You can use it on its own, no need to use the whole lift framework. For the examples above I used the follwing sbt script:
scalaVersion := "2.9.1"
libraryDependencies += "net.liftweb" %% "lift-common" % "2.5-RC2"
libraryDependencies += "net.liftweb" %% "lift-util" % "2.5-RC2"
These are my 2cents:
def sequence[A, M[_] <: TraversableOnce[_]](in: M[Try[A]])
(implicit cbf:CanBuildFrom[M[Try[A]], A, M[A]]): Try[M[A]] = {
in.foldLeft(Try(cbf(in))) {
(txs, tx) =>
for {
xs <- txs
x <- tx.asInstanceOf[Try[A]]
} yield {
xs += x
}
}.map(_.result())
}