Using map on an Option[Seq[T]] - scala

I have an Option[Seq[T]] which, naturally enough may contain a Seq[T] or may indeed by None.
I have been warned away from using .get but how can I use map to return either the intended populated Seq[T] or an empty List() if the Option was None.
I have managed to do it using pattern matching, was wondering if there is a way to use map to achieve the same goal. Thanks!
val maybeProducts:Option[Seq[Product]] = {....}
val products:Seq[Product] = {
maybeProducts match {
case Some(ps) => ps
case None => List()
}
}

You can use getOrElse:
maybeProducts.getOrElse(List())

val products: Seq[Product] = maybeProducts.getOrElse(List())

For the record; another solution is to convert the option to list and then flatten them:
maybeProducts.toList.flatten

Related

Getting max from a case class List of nodes, returning Scala Option[List]

I have a case class Node that I have written, and create a list from it and I need to find the node that has maximum disk.
I wrote the below code, is there a better way of doing it? Also, in my actual production code, my "nodeList" variable will be not just Option[List[Node]] but Future[Option[List[Node]]]. I guess still the answer/code won't change much except for the fact that I will do a map/flatMap to go inside the future and do the same thing.
If anyone has a better suggestion to write below code more Scala way, please share your thoughts.
scala> case class Node(disk: Integer, name: String)
defined class Node
scala> val nodeList = Option(List(Node(40, "node1"), Node(200, "node3"),Node(60, "node2")))
nodeList: Option[List[Node]] = Some(List(Node(40,node1), Node(200,node3), Node(60,node2)))
scala> val maxDisk = nodeList match {
| case None => println("List is empty"); None
| case Some(lst) => {
| Some(lst.max(Ordering.by((_:Node).disk)))
| }
| }`
maxDisk: Option[Node] = Some(Node(200,node3))
Judging by the code you wrote, I'm not sure if you really should use Optional[List[Node]]. You seem to treat None as an empty List, and you don't check for the empty list in the Some case. You might want to see if just a plain List[Node] suits your use better (where None would become Nil, and Some(lst) is just lst, and the unused Some(Nil) case no longer exists to confuse anyone).
If you do keep Optional[List[Node]], I'd do it like this:
nodeList
.filterNot(_.isEmpty) // maxBy throws if the list is empty; check for it
.map(_.maxBy(_.disk)) // maxBy looks nicer than max(Ordering.by)
If you switch to List[Node], it's slightly uglier:
Some(nodeList)
.filterNot(_.isEmpty) // We're using the filter utility of Option here,
.map(_.maxBy(_.disk)) // so I wrap with Some to get access to filterNot.
You can use recursion with List pattern matching.
case class Node(disk: Integer, name: String)
val nodeList = Option(List(Node(40, "node1"), Node(200, "node3"),Node(60, "node2")))
def findMaxValue(list: List[Node]): Option[Node] = list match {
case Nil => None
case List(x) => Some(x)
case first :: second :: rest => if(first.disk > second.disk) findMaxValue(first::rest) else findMaxValue(second::rest)
}
val node:Option[Node] = findMaxValue(nodeList.getOrElse(Nil))
println(node.get.disk) //print 200

Do something when exactly one option is non-empty

I want to compute something if exactly one of two options is non-empty. Obviously this could be done by a pattern match, but is there some better way?
(o1, o2) match {
case (Some(o), None) => Some(compute(o))
case (None, Some(o)) => Some(compute(o))
case _ => None
}
You could do something like this:
if (o1.isEmpty ^ o2.isEmpty)
List(o1,o2).flatMap(_.map(x=>Some(compute(x)))).head
else
None
But pattern matching is probably the better way to go.
Thanks to helpful comments from #Suma, I came up with another solutions in addition to the current ones:
Since the inputs are always in the form of Option(x):
Iterator(Seq(o1,o2).filter(_!=None))
.takeWhile(_.length==1)
.map( x => compute(x.head.get))
.toSeq.headOption
Using iterator also allows for a sequence of values to be passed to the input. The final mapping will be done if and only if one value in the sequence is defined.
Inspired by now deleted answer of pedrofurla, which was attempting to use o1 orElse o2 map { compute }, one possibility is to define xorElse, the rest is easy with it:
implicit class XorElse[T](o1: Option[T]) {
def xorElse[A >: T](o2: Option[A]): Option[A] = {
if (o1.isDefined != o2.isDefined) o1 orElse o2
else None
}
}
(o1 xorElse o2).map(compute)
Another possibility I have found is using a pattern match, but using Seq concatenation so that both cases are handled with the same code. The advantage of this approach is it can be extended to any number of options, it will always evaluate when there is exactly one:
o1.toSeq ++ o2 match {
case Seq(one) => Some(compute(one))
case _ => None
}
Just initialize a sequence and then flatten
Seq(o1, o2).flatten match {
case Seq(o) => Some(compute(o))
case _ => None
}

Loop and create list in Scala

I am getting empty list when I am trying to create the list with :: operator. My code looks like this:
def getAllInfo(locks: List[String]): List[LockBundle] = DB.withTransaction { implicit s =>
val myList = List[LockBundle]()
locks.foreach(
l => findForLock(l) :: myList
)
myList
}
def findForLock(lock: String): Option[LockBundle] = { ... }
Any suggestion?
Use flatMap
locks.flatMap(l => findForLock(l))
Your code becomes
def getAllInfo(locks: List[String]): List[LockBundle] = DB.withTransaction { implicit s =>
locks.flatMap(l => findForLock(l))
}
Alternatively you could use map and flatten. Something like this locks.map(l => findForLock(l)).flatten
Functional programming is all about transformations. You just have to transform your existing list into another list using a transformation which is your function findForLock.
Problem with your code
val myList = List[LockBundle]()
locks.foreach(
l => findForLock(l) :: myList
)
myList
First of all foreach returns Unit so, you use foreach for side effecting operations and not transformations. As you need transformation so do not use foreach.
Next, findForLock(l) :: myList gives you a value but this gets ignored as there is no one who is storing the value generated. So, in order to store the value use accumulator and pass it as a function parameter in case of recursion.
Correcting your code
If you want to do in your way. You need to use the accumulator.
First fix your types findForLock(l) returns Option, You list is of type List[LockBundle] so change the list type to List[Option[LockBundle]].
In order to get List[LockBundle] from List[Option[LockBundle]] Just do flatten on List[Option[LockBundle]] list. See below code snippet
var myList = List[Option[LockBundle]]()
locks.foreach(
l => myList = findForLock(l) :: myList
)
myList.flatten
The above way is not functional and is not recommended.
Your code doesn't work, because foreach combinator calls given closure for each element, but all you do here is to return expression findForLock(l) :: myList which is discarded.
As pamu suggested, you can use flatMap on a function to map each element to values returned by findForLock and flatten that list, which turns Option into element of the list if it's Some or nothing if it's None.
Keep in mind that this works only because there is an implicit conversion from Option to Seq, in general flatMap works only if you return the same type as given monad (that in this case is List or Option).

how to flatten disjunction type

If I have a following method
def getMyList :\/[Throwable,List[\/[Throwable,Int]]] ={
....
}
how to flatten type of getMyList to \/[Throwable,List[Int]]
Just flatMap and sequenceU, it's all in scalaz:
def flatten(e: \/[Throwable,List[\/[Throwable,Int]]]): \/[Throwable,List[Int]] = {
e.flatMap(a => a.sequenceU)
}
If by flatten, you mean remove the left types from List[\/[Throwable,Int]], then you can map the outer disjunction, and collect the right types:
list.map(_.collect{ case \/-(x) => x})
I don't think that some higher order "flatten" exists for /. Looks like Validateion & ValidationNEL will be better choice for this problem. However here is "dirty" solution for /, it will return first fail. If you want to accumulate failures Validation is way to go
val getMyList: \/[Throwable,List[\/[Throwable,Int]]] =
//\/-(List(-\/(new RuntimeException("test")), \/-(1)))
\/-(List(\/-(2), \/-(1)))
val flatten = getMyList.fold(\/.left, _.foldLeft(\/.right[Throwable, List[Int]](List.empty[Int])) {
case (\/-(list), \/-(i)) => \/-(list :+ i)
case (\/-(list), -\/(err)) => -\/(err)
case (-\/(err), _) => -\/(err)
})
println(flatten)
We use the following method, where .sSuccess creates a \/[_, Seq[T]] and .sFail creates a \/[Throwable, _] with all of the throwables' error messages concatenated:
implicit class CondenseEither[T](seq: Seq[\/[Throwable,T]]) = {
def condenseSeq: \/[Throwable, Seq[T]] = {
val errs = seq.filter(_.isLeft).map(_.toEither)
if(errs.isEmpty) seq.map(_.toEither).map(_.right.get).sSuccess
else errs.map(_.left.get.getMessage).mkString(", ")).sFail
}
}
There's probably a way to do this without the toEithers

Handle Scala Option idiomatically

What is the more idiomatic way to handle an Option, map / getOrElse, or match?
val x = option map {
value => Math.cos(value) + Math.sin(value)
} getOrElse {
.5
}
or
val x = option match {
case Some(value) => Math.cos(value) + Math.sin(value)
case None => .5
}
You could always just look at the Scaladoc for Option:
The most idiomatic way to use an scala.Option instance is to treat it as a collection or monad and use map,flatMap, filter, or foreach:
val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")
And a bit later:
A less-idiomatic way to use scala.Option values is via pattern matching:
val nameMaybe = request getParameter "name"
nameMaybe match {
case Some(name) =>
println(name.trim.toUppercase)
case None =>
println("No name value")
}
Use fold for this kind of map-or-else-default thing:
val x = option.fold(0.5){ value => Math.cos(value) + Math.sin(value) }
Obviously both are valid and I don't think one is more idiomatic than the other. That being said, using map uses the fact the Option is a Monad. This can be particularly advantageous when combining two Options. Say you have two Option[Int] that you would like to add. In this case instead of doing multiple matches it is much cleaner to use map/flatMap and it's equivalent "for comprehensions". So for your example both are valid... but for other examples using map/flatMap is often much more succinct.
Some(6).flatMap(intValue => Some(5).map(intValue + _))
or
for {
i <- Some(6)
j <- Some(5)
} yield i + j
All of them have different semantics, so in your case none of them.
map applies some function to the value inside Option, if it exists (Some, not None). Basically this is how you safely work with Options, appling function on some null value is dangeroues, cause it can throw NPE, but in case with Option it just returns None.
getOrElse simply returns either it's value or default one (which you provide as an argument). It won't do anything with the value inside the Option, you can just extract it, if you have Some, or return a default one, in case of None.
and match approach i'd say is a combination of two, cause you can apply some computation on the values and extract it from the Option