Can someone help me convert this to a flatMap or for-comprehension? I know how to handle more trivial scenarios of nested option.
case class Person(name: String, signficantOther: Option[String])
val nightclubPeoples : Option[Seq[Person]] = ???
def significantOthers(nightClubPeoples : Option[Seq[Person]]) : List[String] = {
nightclubPeoples match {
case Some(x) => x map { y : Person =>
y.significantOther match {
case Some(z) => z
case None => "No Signficant Other"
}
}.toList
case None => Nil
}
}
I think I would use a fold:
nightclubPeoples.fold[List[String]](Nil)(_.flatMap(_.signficantOther)(collection.breakOut))
Sascha Kolberg's answer is a, as I think, better solution(just omit the case when signficantOther is empty, i.e "No Signficant Other" will not in the result List) for your question, however since
you demand "convert this to a flatMap or for-comprehension", so the code is as follows.
case class Person(name: String, signficantOther: Option[String])
val nightClubPeopleOpt : Option[Seq[Person]] = Some(Person("name", Some("1")) :: Person("name1", None) :: Nil)
def significantOthers(nightClubPeopleOpt : Option[Seq[Person]]) = {
for {
nightClubPeople <- nightClubPeopleOpt
} yield {
// scenario 1
// if signficantOther is empty, then it should be ignored
// Answer -> List(1)
nightClubPeople.flatMap(_.signficantOther).toList
// scenario 2
// if signficantOther is empty, then we use the default "No Signficant Other"
// Answer -> List(1, No Signficant Other)
nightClubPeople.map { nightClubPerson =>
nightClubPerson.signficantOther.getOrElse("No Signficant Other")
}.toList
}
}.getOrElse(Nil)
println(significantOthers(nightClubPeopleOpt))
Here you go:
nightclubPeoples.toSeq.flatMap(_.map(_.signficantOther.getOrElse("No Signficant Other")))
A relatively readable direct transformation would be:
def significantOthers(nightClubPeoples : Option[Seq[Person]]): List[String] = {
for {
nightClubPeoples <- nightClubPeoples.toList
person <- nightClubPeoples
} yield person.significantOther.getOrElse("No Significant Other")
}
The thing to note here is that we convert nightClubPeoples to a list up front, allowing us to use a for comprehension all along.
However, I doubt you really want to have "No Significant Other" appear in your list when signficantOther is None. Rather, it should be ignored, which would give the even simpler:
def significantOthers(nightClubPeoples : Option[Seq[Person]]): List[String] = {
for {
nightClubPeoples <- nightClubPeoples.toList
person <- nightClubPeoples
significantOther <- person.significantOther
} yield significantOther
}
Related
I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???
I have a match statement like this:
val x = y match {
case array: Array[Float] => call z
case array: Array[Double] => call z
case array: Array[BigDecimal] => call z
case array: Array[_] => show error
}
How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.
Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:
List(1,2,3) match {
case (a:Int) :: tail => println("yep")
}
This work, although not very nice:
def x(y: Array[_]) = y match {
case a if a.isInstanceOf[Array[Double]] ||
a.isInstanceOf[Array[Float]] ||
a.isInstanceOf[Array[BigDecimal]] => "call z"
case _ => "show error"
}
Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:
def x(y: Array[_ <: Any]) = y match {
case a # (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
case a: Array[_] => "show error"
}
May be it helps a bit:
import reflect.runtime.universe._
object Tester {
def test[T: TypeTag](y: Array[T]) = y match {
case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
case c: Array[_] => "oh"
}
}
scala> Tester.test(Array(1,2,3))
res0: String = hi
scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi
scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:
if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
// call z
} else {
// show error
}
Not particularly Scala'ish, though; I think #thoredge's answer is the best for that.
You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:
def x(y: Array[_]) = {
y.isEmpty match {
case true => "error"
case false => y.head match {
case a:Double | a:BigInt => do whatever
case _ => "error"
}
}
}
I wonder how I can transform a List[Future[String\/String]] to Future[String\/String].
In fact, I just want to apply for each element of the list EitherT.apply.
What I want looks like this :
val result: EitherT[Future, String, String] =
for{
_ <- EitherT.apply(fun(arg1))
_ <- EitherT.apply(fun(arg2))
res <- EitherT.apply(fun(arg3))
} yield res
result.run
// ...
def fun(arg1: MyType): Future[String\/String] = ...
In this example, I would like to set as input the function fun and a list (of n elements) of arguments for the function fun (here : List(arg1, arg2, arg3)).
But I don't know how to manage with Futures.
Is it possible to keep the asynchrony ?
I have found a way to do it but without using EitherT.apply.
def seqCheck(elements: List[Element]): Future[String\/String] = elements match {
case element :: tail =>
fun(element) flatMap {
case \/-(_) =>
seqCheck(tail)
case -\/(e) => Future{
-\/(e)
}
}
case Nil =>
Future {
\/-("Ok")
}
}
It not as nice as I would like it to be but it seems to work.
If somebody knows a best way...
Let's say you've got a bunch of methods:
def foo() : Try[Seq[String]]
def bar(s:String) : Try[String]
and you want to make a for-comprhension:
for {
list <- foo
item <- list
result <- bar(item)
} yield result
of course this won't compile since Seq cannot be used with Try in this context.
Anyone has a nice solution how to write this clean without breaking it into separate two for's?
I've came across this syntax problem for the thirds time and thought that it's about time to ask about this.
IMHO: Try and Seq is more than what you need to define a monad transformer:
Code for library:
case class trySeq[R](run : Try[Seq[R]]) {
def map[B](f : R => B): trySeq[B] = trySeq(run map { _ map f })
def flatMap[B](f : R => trySeq[B]): trySeq[B] = trySeq {
run match {
case Success(s) => sequence(s map f map { _.run }).map { _.flatten }
case Failure(e) => Failure(e)
}
}
def sequence[R](seq : Seq[Try[R]]): Try[Seq[R]] = {
seq match {
case Success(h) :: tail =>
tail.foldLeft(Try(h :: Nil)) {
case (Success(acc), Success(elem)) => Success(elem :: acc)
case (e : Failure[R], _) => e
case (_, Failure(e)) => Failure(e)
}
case Failure(e) :: _ => Failure(e)
case Nil => Try { Nil }
}
}
}
object trySeq {
def withTry[R](run : Seq[R]): trySeq[R] = new trySeq(Try { run })
def withSeq[R](run : Try[R]): trySeq[R] = new trySeq(run map (_ :: Nil))
implicit def toTrySeqT[R](run : Try[Seq[R]]) = trySeq(run)
implicit def fromTrySeqT[R](trySeqT : trySeq[R]) = trySeqT.run
}
and after you can use for-comrehension (just import your library):
def foo : Try[Seq[String]] = Try { List("hello", "world") }
def bar(s : String) : Try[String] = Try { s + "! " }
val x = for {
item1 <- trySeq { foo }
item2 <- trySeq { foo }
result <- trySeq.withSeq { bar(item2) }
} yield item1 + result
println(x.run)
and it works for:
def foo() = Try { List("hello", throw new IllegalArgumentException()) }
// x = Failure(java.lang.IllegalArgumentException)
You can take advantage of the fact that Try can be converted to Option, and Option to Seq:
for {
list <- foo.toOption.toSeq // toSeq needed here, as otherwise Option.flatMap will be used, rather than Seq.flatMap
item <- list
result <- bar(item).toOption // toSeq not needed here (but allowed), as it is implicitly converted
} yield result
This will return a (possibly empty, if the Trys failed) Seq.
If you want to keep all the exception detail, you'll need a Try[Seq[Try[String]]]. This can't be done with a single for comprehension, so you're best sticking with plain map:
foo map {_ map bar}
If you want to mingle your Trys and Seqs in a different way, things get fiddlier, as there's no natural way to flatten a Try[Seq[Try[String]]]. #Yury's answer demonstrates the sort of thing you'd have to do.
Or, if you're only interested in the side effects of your code, you can just do:
for {
list <- foo
item <- list
result <- bar(item)
} result
This works because foreach has a less restrictive type signature.
A Try can be converted to an Option, which you can than use in a for-comprehension. E.g.
scala> def testIt() = {
| val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
| dividend.toOption
| }
testIt: ()Option[Int]
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
1522756
First time I entered "w", then second time 1234.
I would prefer to see just the value of the Option (if it were not None) , instead of the following additional Some() noise:
List((Some(OP(_)),Some(share),3), (Some(OP(D)),Some(shaara),4), (Some(OP(I)),Some(shaaee),4))
Now, I could write a method that handles this for List[Option[_]] .. But there are many other structures in which Options appear - so this approach of addressing explicitly each one is cumbersome.
Due to implicits having lower precedence the following code simply gets ignored:
implicit def toString(myopt : Option[_]) = if (myopt == None) "None" else myopt.get
The concern is that - although implementing for example a toString(List[Option_]]) method that handles this in the desired manner, that is still a one-off. What about a
Map[Option,Option] => def toString(Map[Option,Option]) = { .. }
It seems We would still need to implement an explicit toString() for each collection type..
I guess you can't override this behavior for toString, but you could use shows (and show) methods from scalaz. You could override behavior of these methods:
import scalaz._, Scalaz._
val l = List(1.some, none, 3.some, 4.some, none, 6.some)
l.shows
// res0: String = [Some(1),None,Some(3),Some(4),None,Some(6)]
implicit def optionShow[T: Show]: Show[Option[T]] =
Show.show{ _.map{_.show}.getOrElse(Cord("<none>")) }
l.shows
// res1: String = [1,<none>,3,4,<none>,6]
It works for all types with Show:
1.some.node(none.node(2.some.leaf)).drawTree
// 1
// |
// `- <none>
// |
// `- 2
Map(1.some -> 2.some, none[Int] -> 3.some).shows
// Map[1->2, <none>->3]
Well i would just write a Wrapper similar to your implicit.
class OW[T](val option : Option[T]) {
override def toString = if (option.isEmpty) "None" else option.get.toString
}
Then when i want the pretty toString, i would just map any collection to instances of OW.
println(List(Some(3), Some("Hello"), None).map(new OW(_)))
which prints: List(3, Hello, None)
Don't really see a better way.
Following takes care of the cases that came to mind:
def show(obj: Any) : String = {
obj match {
case o: Option[_] =>
if (o == None) {
"<none>"
} else {
show(o.get)
}
case i: Iterable[_] =>
i.map(show).mkString("[", ",", "]")
case m: Map[_, _] =>
m.map {
case (a, b) => List(show(a), show(b)).mkString(":")
}.mkString("{", ",", "}")
case e: Enumeration =>
e.toString
case c : Product if !c.getClass.getMethods.map(_.getName).contains("copy$default$2") =>
c.toString
case t: Product =>
t.productIterator.map(a => show(a)).mkString("(", ",", ")")
case _ =>
if (obj.isInstanceOf[AnyRef])
obj.asInstanceOf[AnyRef].toString
else
"" + obj
}
}
I opted to code this up to avoid adding scalaz dependency (assuming their Show class supports similar feature)