I have code as:
Option.when(condition)(evaluation)
..and I want to add another condition if first one fails, basically pseudo code like:
if(condition) {
Some(evaluation)
} else if(another condition) {
Some(another evaluation)
} else None
So what's the accepted way of chaining multiple Option.when?
You can use Option.orElse
Option.when(false)(1).orElse(Option.when(false)(2)).orElse(Option.when(true)(3)) // Some(3)
// that's to say
val conditions: List[Boolean] = List(false, false, true)
val values: List[Int] = List(1,2,3)
val result: Option[Int] = conditions.zip(values).map {
case (cond, value) => Option.when(cond)(value)
}.reduceLeft(_ orElse _) // Some(3)
UPDATE: use lazy evaluate example
val n = 7
def randomBooleans(): Iterator[Boolean] =
Iterator.continually(scala.util.Random.nextBoolean()).take(n)
def randomValues(): Iterator[Int] =
Iterator.continually(scala.util.Random.nextInt()).take(n)
val res = randomBooleans()
.zip(randomValues())
.foldLeft(Option.empty[Int]) {
case (res, (cond, value)) => {
res.orElse(if (cond) {
println(s"${cond} ${value}")
Some(value)
} else {
println(s"${cond} ${value}")
None
})
}
}
println(s"result ${res}")
// false -1035387186
// false -1516857504
// true 1775201252
// result Some(1775201252)
Related
If I were splitting a string, I would be able to do
"123,456,789".split(",")
to get
Seq("123","456","789")
Thinking of a string as a sequence of characters, how could this be generalized to other sequences of objects?
val x = Seq(One(),Two(),Three(),Comma(),Five(),Six(),Comma(),Seven(),Eight(),Nine())
x.split(
number=>{
case _:Comma => true
case _ => false
}
)
split in this case doesn't exist, but it reminds me of span, partition, groupby, but only span seems close, but it doesn't handle leading/ending comma's gracefully.
implicit class SplitSeq[T](seq: Seq[T]){
import scala.collection.mutable.ListBuffer
def split(sep: T): Seq[Seq[T]] = {
val buffer = ListBuffer(ListBuffer.empty[T])
seq.foreach {
case `sep` => buffer += ListBuffer.empty
case elem => buffer.last += elem
}; buffer.filter(_.nonEmpty)
}
}
It can be then used like x.split(Comma()).
The following is 'a' solution, not the most elegant -
def split[A](x: Seq[A], edge: A => Boolean): Seq[Seq[A]] = {
val init = (Seq[Seq[A]](), Seq[A]())
val (result, last) = x.foldLeft(init) { (cum, n) =>
val (total, prev) = cum
if (edge(n)) {
(total :+ prev, Seq.empty)
} else {
(total, prev :+ n)
}
}
result :+ last
}
Example result -
scala> split(Seq(1,2,3,0,4,5,0,6,7), (_:Int) == 0)
res53: Seq[Seq[Int]] = List(List(1, 2, 3), List(4, 5), List(6, 7))
This is how I've solved it in the past, but I suspect there is a better / more elegant way.
def break[A](xs:Seq[A], p:A => Boolean): (Seq[A], Seq[A]) = {
if (p(xs.head)) {
xs.span(p)
}
else {
xs.span(a => !p(a))
}
}
Let's say we have a sequence of Scala Future results like val futures: Seq[Future[Boolean]] and let's say I'd like to get a Future[Boolean] that returns the first true result, but otherwise fails or returns false normally.
At the moment the current implementation I have is something like this:
// convert to Seq[Future[Try[Boolean]]]
val tries = futures.map{ _.map(Success(_)).recover{ case t: Throwable => Failure(t) } }
val sequence = Future.sequence(tries)
// first positive
val positive = sequence.map{ _.find{ case Success(true) => true; case _ => false } }
val nonpositive = sequence.map{ _.find{ case Success(true) => false; case _ => true } }
// prioritise positive results over other results
val futureTry =
for {
p <- positive
n <- nonpositive
} yield( p.orElse(n) )
// convert from Future[Try[Boolean]] back to Future[Boolean]
val future = futureTry.map { _.map { t => t.get } }
Is there a simpler way to write the above?
You can use Future.find, and since you are looking for a Boolean, you can pass the identity function :
import scala.concurrent.Future
val failedF = Future.failed[Boolean](new Exception("boo"))
val falseF = Future.successful(false)
val trueF = Future.successful(true)
val f1 = Future.find(trueF :: falseF :: failedF :: Nil)(identity)
val f2 = Future.find(falseF :: failedF :: Nil)(identity)
val f3 = Future.find(failedF :: Nil)(identity)
Future.sequence(f1 :: f2 :: f3 :: Nil).foreach(println)
// List(Some(true), None, None)
It returns Future[Option[Boolean]] like it is, but you could easily turn it into Future[Boolean] by supplying a default value with getOrElse :
f2.map(_.getOrElse(false)).foreach(println)
// false
I have multiple Option's. I want to check if they hold a value. If an Option is None, I want to reply to user about this. Else proceed.
This is what I have done:
val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
case None => true
case _ => false
})
i match{
case Some(x) => Ok("Bad Request")
case None => {
//move forward
}
}
Above I can replace find with contains, but this is a very dirty way. How can I make it elegant and monadic?
Edit: I would also like to know what element was None.
Another way is as a for-comprehension:
val outcome = for {
nm <- name
em <- email
pwd <- pass
result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)
This will generate outcome as a Some(result), which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). Eg:
outcome.map(Ok(result)).orElse(Ok("Bad Request"))
val ok = Seq(name, email, pass).forall(_.isDefined)
If you want to reuse the code, you can do
def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)
If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go.
def findMissingValues(v: (String, Option[_])*) = v.collect {
case (name, None) => name
}
val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)
if(missingValues.isEmpty) {
Ok(...)
} else {
BadRequest("Missing values for " + missingValues.mkString(", ")))
}
val response = for {
n <- name
e <- email
p <- pass
} yield {
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
Or, with Scalaz:
val response = (name |#| email |#| pass) { (n, e, p) =>
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
// bad request
}
I think the most straightforward way would be this:
(name,email,pass) match {
case ((Some(name), Some(email), Some(pass)) => // proceed
case _ => // Bad request
}
A version with stone knives and bear skins:
import util._
object Test extends App {
val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
def verify(fields: List[Option[String]]) = {
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) =>
val u = i match {
case 0 => t copy (_1 = s)
case 1 => t copy (_2 = s)
case 2 => t copy (_3 = s)
}
Right(u)
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
val res = verify(List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify(List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
}
The same thing, using reflection to generalize the tuple copy.
The downside is that you must tell it what tuple to expect back. In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years.
def verify[A <: Product](fields: List[Option[String]]) = {
import scala.reflect.runtime._
import universe._
val MaxTupleArity = 22
def tuple = {
require (fields.length <= MaxTupleArity)
val n = fields.length
val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
val init = tupleN.typeSignature member nme.CONSTRUCTOR
val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
val vs = Seq.fill(n)(null.asInstanceOf[String])
ctor(vs: _*).asInstanceOf[Product]
}
def zero: Either[List[Int], Product] = Right(tuple)
def nextProduct(p: Product, i: Int, s: String) = {
val im = currentMirror reflect p
val ts = im.symbol.typeSignature
val copy = (ts member TermName("copy")).asMethod
val args = copy.paramss.flatten map { x =>
val name = TermName(s"_$i")
if (x.name == name) s
else (im reflectMethod (ts member x.name).asMethod)()
}
(im reflectMethod copy)(args: _*).asInstanceOf[Product]
}
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) => Right(nextProduct(t, i + 1, s))
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}.asInstanceOf[Either[List[Int], A]]
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
type T3 = Tuple3[String,String,String]
val res = verify[T3](List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify[T3](List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
I know this doesn't scale well, but would this suffice?
(name, email, pass) match {
case (None, _, _) => "name"
case (_, None, _) => "email"
case (_, _, None) => "pass"
case _ => "Nothing to see here"
}
I have this setup where I want to populate an object fields from an XML NodeSeq. A cutdown version of my setup is shown. It works well, but when I put None in testObj, I get a java.util.NoSuchElementException: None.get.
The problem I believe is that I cannot find a way to get the class of the Option[_] when it is None. I've been stuck on this for ages and I cannot see a way out. I feel there must be a way to branch and populate the object field when the original value is None, but how?
import xml.NodeSeq
object test {
case class TestObject(name: Option[String] = None, value: Option[Double] = None)
def main(args: Array[String]): Unit = {
val testObj = TestObject(Some("xxx"), Some(12.0))
// val testObj = TestObject(None, Some(12.0))
val nodeSeq = <test> <name> yyy </name> <value> 34.0 </value> </test>
val result = getObject[TestObject](nodeSeq, Option(testObj))
println("result = " + result) // result = Some(TestObject(Some(yyy),Some(34.0)))
}
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if ((nodeSeq.isEmpty) || (!obj.isDefined)) None else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val fieldValue = field.get(obj.get)
if (fieldValue == null || !fieldValue.isInstanceOf[Option[_]]) None
var newValue: Option[_] = None
fieldValue.asInstanceOf[Option[_]].get match { // <---- None.get is the error here
case x: Double => newValue = Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => newValue = Some((nodeSeq \ field.getName).text.trim)
}
val decField = obj.get.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(obj.get, newValue)
}
obj
}
}
}
I'm ignoring some other parts of this code that I don't like, however, you can't call .get on a None. You can call map and it will return what I assume you want. Here's the modified code:
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if (nodeSeq.isEmpty || obj.isEmpty) {
None
}
else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val fieldValue = field.get(obj.get)
if (fieldValue == null || !fieldValue.isInstanceOf[Option[_]]) None
val newValue = fieldValue.asInstanceOf[Option[_]].map(a => {
a match {
case x: Double => Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => Some((nodeSeq \ field.getName).text.trim)
}
})
val decField = obj.get.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(obj.get, newValue)
}
obj
}
}
In practice, I usually avoid calling .get on options as it results in bugs quite often.
UPDATE:
This version is a little more functional, though it still alters the obj that you send in:
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
obj.map(o => {
for {
field <- o.getClass.getDeclaredFields
_ = field.setAccessible(true)
fieldValue <- Option(field.get(o))
} yield {
val newValue = fieldValue match {
case Some(a) => {
a match {
case x: Double => Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => Some((nodeSeq \ field.getName).text.trim)
}
}
case _ => None
}
val decField = o.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(o, newValue)
}
o
})
}
Actually, this might be closer to what you want: check the type of the field instead.
import xml.NodeSeq
object test {
case class TestObject(name: Option[String] = None, value: Option[Double] = None)
def main(args: Array[String]): Unit = {
val testObj = TestObject(Some("xxx"), Some(12.0))
// val testObj = TestObject(None, Some(12.0))
val nodeSeq = <test> <name> yyy </name> <value> 34.0 </value> </test>
val result = getObject[TestObject](nodeSeq, Option(testObj))
println("result = " + result) // result = Some(TestObject(Some(yyy),Some(34.0)))
}
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if ((nodeSeq.isEmpty) || (!obj.isDefined)) None else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val newValue = field.getGenericType match {
case t: ParametrizedType if (t.getActualType == classOf[Option[_]]) =>
val paramType = t.getActualTypeArguments()(0)
if (paramType == classOf[java.lang.Double]) // since Double can't be a generic type parameter on JVM
// from your original code, but it probably needs to be modified
// to handle cases where field isn't in nodeSeq or not a number
Some((nodeSeq \ field.getName).text.trim.toDouble)
else if (paramType == classOf[String])
Some((nodeSeq \ field.getName).text.trim)
else None
case _ => None
}
if (newValue.isDefined)
field.set(obj.get, newValue)
}
obj
}
}
}
Version creating a new object instead (might need modification for your requirements):
def getObject[A](nodeSeq: NodeSeq, objClass: Class[A]): Option[A] = {
if (nodeSeq.isEmpty) None else {
try {
val fieldValues = objClass.getDeclaredFields.flatMap { field =>
field.setAccessible(true)
field.getGenericType match {
case t: ParametrizedType if (t.getActualType == classOf[Option[_]]) =>
val paramType = t.getActualTypeArguments()(0)
if (paramType == classOf[java.lang.Double])
Some(Some((nodeSeq \ field.getName).text.trim.toDouble))
else if (paramType == classOf[String])
Some(Some((nodeSeq \ field.getName).text.trim))
else None
case _ => None
}
}
// assumes only one constructor, otherwise get the desired one
val ctor = objClass.getConstructors()(0)
Some(ctor.newInstance(fieldValues))
} catch {
case _ => None
}
}
}
I am working on a method that has 3 possible outcomes for multiple items: Error, Invalid and Success. For each of these I need to return a json list identifying which items were in error, invalid and successful.
My current attempt follows. I have used Object to represent the class my objects are as fully explaining would take too long. The Object class has a method process which returns a boolean to indicate success or error and throws an exception when the object is invalid:
def process(list: List[Objects]) = {
val successIds = new ListBuffer[Int]();
val errorIds = new ListBuffer[Int]();
val invalidIds = new ListBuffer[Int]();
list.foreach( item => {
try {
if (item.process) {
successIds ++ item.id
} else {
errorIds ++ item.id
}
} catch {
case e: Exception => invalidIds ++ item.id
}
})
JsonResult(
Map("success" -> successIds,
"failed" -> errorIds,
"invalid" -> invalidIds)
)
}
Problem is using Mutable data structures isn't very "Scala-y". I would prefer to build up these lists in some more functional way but I am quite new to scala. Any thoughts or hints as to how this might be done?
My though is using something like the flatMap method that takes a tuple of collections and collates them in the same way the flatMap method does for a single collection:
def process(list: List[Objects]) = {
val (success, error, invalid) = list.flatMap( item => {
try {
if (item.process) {
(List(item.id), List.empty, List.empty)
} else {
(List.empty, List(item.id), List.empty)
}
} catch {
case e: Exception =>
(List.empty, List.empty, List(item.id))
}
})
JsonResult(
Map("success" -> success,
"failed" -> error,
"invalid" -> invalid)
)
}
flatMap isn't what you need here - you need groupBy:
def process(list: List[Objects]) = {
def result(x: Objects) =
try if (x.process) "success" else "failed"
catch {case _ => "invalid"}
JsonResult(list groupBy result mapValues (_ map (_.id)))
}
There's always recursion:
class Ob(val id: Int) { def okay: Boolean = id < 5 }
#annotation.tailrec def process(
xs: List[Ob],
succ: List[Int] = Nil,
fail: List[Int] = Nil,
invalid: List[Int] = Nil
): (List[Int], List[Int], List[Int]) = xs match {
case Nil => (succ.reverse, fail.reverse, invalid.reverse)
case x :: more =>
val maybeOkay = try { Some(x.okay) } catch { case e: Exception => None }
if (!maybeOkay.isDefined) process(more, succ, fail, x.id :: invalid)
else if (maybeOkay.get) process(more, x.id :: succ, fail, invalid)
else process(more, succ, x.id :: fail, invalid)
}
Which works as one would hope (skip the reverses if you don't care about order):
scala> process(List(new Ob(1), new Ob(7), new Ob(2),
new Ob(4) { override def okay = throw new Exception("Broken") }))
res2: (List[Int], List[Int], List[Int]) = (List(1,2),List(7),List(4))
Adapted to make it compile without "Objects"
def procex (item: String): Boolean = ((9 / item.toInt) < 1)
def process (list: List[String]) = {
val li: List[(Option[String], Option[String], Option[String])] = list.map (item => {
try {
if (procex (item)) {
(Some (item), None, None)
} else {
(None, Some (item), None)
}
} catch {
case e: Exception =>
(None, None, Some (item))
}
})
li
}
// below 10 => failure
val in = (5 to 15).map (""+_).toList
// 0 to throw a little exception
val ps = process ("0" :: in)
val succeeders = ps.filter (p=> p._1 != None).map (p=>p._1)
val errors = ps.filter (p=> p._2 != None).map (p=>p._2)
val invalides = ps.filter (p=> p._3 != None).map (p=>p._3)
What doesn't work:
(1 to 3).map (i=> ps.filter (p=> p._i != None).map (p=>p._i))
_i doesn't work.