Basically I'm looking for the most scala-like way to do the following:
def sum(value1: Option[Int], value2: Option[Int]): Option[Int] =
if(value1.isDefined && value2.isDefined) Some(value1.get + value2.get)
else if(value1.isDefined && value2.isEmpty) value1
else if(value1.isEmpty && value2.isDefined) value2
else None
This gives correct output:
sum(Some(5), Some(3)) // result = Some(8)
sum(Some(5), None) // result = Some(5)
sum(None, Some(3)) // result = Some(3)
sum(None, None) // result = None
Yet to sum more than two options I'd have to use way too many ifs or use some sort of loop.
EDIT-1:
While writing the question I came up with sort of an answer:
def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] =
value1.toList ::: value2.toList reduceLeftOption { _ + _ }
This one looks very idiomatic to my inexperienced eye. This would even work with more than two values. Yet is possible to do the same without converting to lists?
EDIT-2:
I ended up with this solution (thanks to ziggystar):
def sum(values: Option[Int]*): Option[Int] =
values.flatten reduceLeftOption { _ + _ }
EDIT-3:
Another alternative thanks to Landei:
def sum(values: Option[Int]*): Option[Int] =
values collect { case Some(n) => n } reduceLeftOption { _ + _ }
How about:
scala> def sum(values: Option[Int]*): Option[Int] = values.flatten match {
| case Nil => None
| case l => Some(l.sum)
| }
sum: (values: Option[Int]*)Option[Int]
scala> sum(Some(1), None)
res0: Option[Int] = Some(1)
scala> sum(Some(1), Some(4))
res1: Option[Int] = Some(5)
scala> sum(Some(1), Some(4), Some(-5))
res3: Option[Int] = Some(0)
scala> sum(None, None)
res4: Option[Int] = None
Edit
Maybe it would be sane to return 0 if all arguments were None. In that case the function would reduce to values.flatten.sum.
scala> def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
| case (Some(x), Some(y)) => Some(x + y)
| case (Some(x), None) => Some(x)
| case (None, Some(y)) => Some(y)
| case _ => None
| }
sum: (a: Option[Int],b: Option[Int])Option[Int]
scala> sum(Some(5), Some(3))
res0: Option[Int] = Some(8)
scala> sum(Some(5), None)
res1: Option[Int] = Some(5)
scala> sum(None, Some(3))
res2: Option[Int] = Some(3)
scala> sum(None, None)
res3: Option[Int] = None
Another solution is:
def sum(values: Option[Int]*): Int = values.collect{case Some(n) => n}.sum
While in the current case flatten is clearly more convenient, the collect version is more flexible, as it allows to perform mappings and to have additional filter conditions or complex patterns. E.g. imagine you want to have the sum of the squares of all even numbers in values:
values.collect{case Some(n) if n mod 2 == 0 => n*n}.sum
You can make it very concise using the fact that there is an Semigroup instance for Option that does exactly what you want. You can use scalaz or cats. Here is an example using cats:
import cats.std.option._
import cats.syntax.semigroup._
import cats.std.int._
Option(1) |+| Option(2) // Some(3)
Option(1) |+| None // Some(1)
None |+| Option(2) // Some(2)
So your sum becomes:
def sum(v1: Option[Int], v2: Option[Int]): Option[Int] = v1 |+| v2
Reduced solution of michael.kebe with a little look to some basic mathematical rules:
def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
case (None,None) => None
case _ => Some(a.getOrElse(0)+b.getOrElse(0))
}
scala> sum(Some(5), Some(3)) // result = Some(8)
res6: Option[Int] = Some(8)
scala> sum(Some(5), None) // result = Some(5)
res7: Option[Int] = Some(5)
scala> sum(None, Some(3)) // result = Some(3)
res8: Option[Int] = Some(3)
scala> sum(None, None) // result = None
res9: Option[Int] = None
Related
Having an Option[Seq[String]], I'm trying to find the best way to add an Option[String] to that collection, resulting again in an Option[Seq[String]]. If the collection is None, but the value to add is Some[String], the result should be Option[Seq[String]] with that single value.
I've come up with two ways to write this, which essentially boil down to the same. I feel like there must be a better way to do this. Any suggestions?
val messages: Option[Seq[String]] = Some(Seq("item"))
val message: Option[String] = Some("item 2")
val opt1: Option[Seq[String]] = message match {
case Some(message) => Some(messages.map(_ :+ message).getOrElse(Seq(message)))
case None => messages
}
val opt2: Option[Seq[String]] = message
.map(msg => Some(messages.map(_ :+ msg).getOrElse(Seq(msg))))
.getOrElse(messages)
println(opt1) // Some(List(item, item 2))
println(opt2) // Some(List(item, item 2))
When messages are available. Convert message to Seq and add it to existing message.
Else convert message to Option[Seq[String]]
messages.map(msgs => msgs ++ message.toSeq)
.orElse(message.map(x => Seq(x)))
clear syntax
messages.map(_ ++ message.toSeq) orElse message.map(Seq(_))
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def addOption[T](messages: Option[Seq[T]], message: Option[T]): Option[Seq[T]] =
messages.map(msgs => msgs ++ message.toSeq)
.orElse(message.map(Seq(_)))
// Exiting paste mode, now interpreting.
addOption: [T](messages: Option[Seq[T]], message: Option[T])Option[Seq[T]]
scala> addOption(Some(Seq(1)), Some(2))
res4: Option[Seq[Int]] = Some(List(1, 2))
scala> addOption(Some(Seq(1)), Some(3))
res5: Option[Seq[Int]] = Some(List(1, 3))
scala> addOption(Some(Seq(1)), None)
res6: Option[Seq[Int]] = Some(List(1))
scala> addOption(None, None)
res7: Option[Seq[Nothing]] = None
scala> addOption(None, Some(1))
res8: Option[Seq[Int]] = Some(List(1))
scala> addOption(None, Some(2))
res9: Option[Seq[Int]] = Some(List(2))
Here is another one:
def push[T](message: Option[T], messages: Option[Seq[T]]): Option[Seq[T]] =
message.map(s => messages.getOrElse(Nil) :+ s).orElse(messages)
which produces:
push(Some("!"), Some(Seq("hello", "world"))) // Some(List(hello, world, !))
push(None, Some(List("hello", "world"))) // Some(List(hello, world))
push(Some("!"), None) // Some(List(!))
push(None, None) // None
Using fold opt2 becomes:
val opt2: Option[Seq[String]] =
message.fold(messages)(msg => Some(messages.fold(Seq(msg))(_ :+ msg)))
[ Automatic conversion by Intellij :) ]
Here's an approach using fold:
def addOptMsg[T](msgs: Option[Seq[T]], msg: Option[T]): Option[Seq[T]] =
msgs.fold( msg.map(Seq(_)) )( s => Option(s ++ msg.toSeq) )
Testing the method:
val messages: Option[Seq[String]] = Some(Seq("item1", "item2"))
val message: Option[String] = Some("item3")
val messages0: Option[Seq[String]] = None
val message0: Option[String] = None
addOptMsg(messages, message)
// res1: Option[Seq[String]] = Some(List(item1, item2, item3))
addOptMsg(messages, message0)
// res2: Option[Seq[String]] = Some(List(item1, item2))
addOptMsg(messages0, message)
// res3: Option[Seq[String]] = Some(List(item3))
addOptMsg(messages0, message0)
// res4: Option[Seq[String]] = None
Is there a predefined function x in Scala that combine 2 Options so that
Some(a) x None => Some(a)
None x Some(b) => Some(b)
None x None => None
Yes, this is the orElse method. It chooses the first defined value, or None if neither is defined.
scala> Some(1) orElse None
res0: Option[Int] = Some(1)
scala> None orElse Some(1)
res1: Option[Int] = Some(1)
scala> None orElse None
res2: Option[Nothing] = None
scala> Some(1) orElse Some(2)
res3: Option[Int] = Some(1)
It's not hard to do it by hand:
scala> val a = Some(1)
a: Some[Int] = Some(1)
scala> val b = Some(2)
b: Some[Int] = Some(2)
scala> Seq(a,b).flatten.headOption
res0: Option[Int] = Some(1)
In the question comments, you mention you can't have Some(a) and Some(b), so what you really have is Option[Either[Int,Int]]. In that case, you can use x.map(_.merge) to get back to Option[Int], eg
scala> val x:Option[Either[Int,Int]] = Some(Left(2))
x: Option[Either[Int,Int]] = Some(Left(2))
scala> x.map(_.merge)
res0: Option[Int] = Some(2)
val one: Option[Int] = None
val two = Some(2)
Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(2) which I want
val one = Some(1)
val two = None
Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(1) which I want
val one: Option[Int] = None
val two: Option[Int] = None
Option(one.getOrElse(two.getOrElse(null))) // Gives me None which I want
val one = Some(1)
val two = Some(2)
Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(1) when I want an exception
I briefly looked into the Either type but it seems like it is for "Representing a value of one of two possible types". Am I missing some data structure or Monad? Essentially I want a explicit (and error throwing if both are valuable) get either one if it avaiable or get None
I don't know any pre built to do that, so here is a function:
def xor[T](x: Option[T], y: Option[T]): Option[T] = (x, y) match {
case (Some(_), None) => x
case (None, Some(_)) => y
case (None, None) => None
case _ => throw new Exception()
}
Here's my version of the function
val options = Seq(one, two).flatten
if (options.size > 1) throw new Exception("two options were provided")
options.headOption
def xor[T](a: Option[T], b: Option[T]) =
a orElse b ensuring (_ => a zip b isEmpty)
I would probably go old school if-else for the simple case.
scala> implicit class optxor[A](val opt: Option[A]) extends AnyVal {
| def xor(other: Option[A]) = if (opt.isEmpty) other else if (other.isEmpty) opt else ??? }
defined class optxor
scala> one xor two
res18: Option[Int] = Some(2)
scala> two xor three
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
at optxor$.xor$extension(<console>:8)
... 33 elided
I would have approached this using a pattern matching function like gzou answered, but here's a one liner:
one.map{x => two.foreach(y => throw new Exception("both defined")); x}.orElse(two)
We can combine the 2 Options as an Iterable with Option's ++ operator, and thus pattern match on iterables instead of tuple of options:
optionA ++ optionB match {
case Seq(x, y) => throw new Exception
case x => x.headOption
}
// None and None => None
// Some(5) and None => Some(5)
// None and Some(5) => Some(5)
// Some(5) and Some(3) => Exception
Note headOption which nicely handles both the list with 1 element and the empty list.
Or,
scala> val one: Option[Int] = None
one: Option[Int] = None
scala> val two = Option(2)
two: Option[Int] = Some(2)
scala> val three = Option(3)
three: Option[Int] = Some(3)
scala> (for (_ <- one; _ <- two) yield ???) orElse one orElse two
res0: Option[Int] = Some(2)
scala> (for (_ <- three; _ <- two) yield ???) orElse three orElse two
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
at $anonfun$1$$anonfun$apply$1.apply(<console>:10)
at $anonfun$1$$anonfun$apply$1.apply(<console>:10)
at scala.Option.map(Option.scala:145)
at $anonfun$1.apply(<console>:10)
at $anonfun$1.apply(<console>:10)
at scala.Option.flatMap(Option.scala:170)
... 33 elided
desugaring to
scala> one flatMap (_ => two) map (_ => ???) orElse one orElse two
res3: Option[Int] = Some(2)
but idiomatically it's hard to imagine doing either of these.
How about this?
import scala.util.Try
object XorOption {
def xorOptions[T](one: Option[T], two: Option[T]): Try[Option[T]] = {
Try {
for (x <- one; y <- two) throw new RuntimeException
one.orElse(two)
}
}
def main(args: Array[String]) {
val testData = List(
(None, None),
(None, Some(2)),
(Some(1), None),
(Some(1), Some(2)))
for (t <- testData) {
println(t + " => " + xorOptions(t._1, t._2))
}
}
}
Output:
(None,None) => Success(None)
(None,Some(2)) => Success(Some(2))
(Some(1),None) => Success(Some(1))
(Some(1),Some(2)) => Failure(java.lang.RuntimeException)
List(Some(1), Some(2)).flatten match {
case x :: Nil => Some(x)
case Nil => None
case _ => throw new Exception
}
Is there a predefined function x in Scala that combine 2 Options so that
Some(a) x None => Some(a)
None x Some(b) => Some(b)
None x None => None
Yes, this is the orElse method. It chooses the first defined value, or None if neither is defined.
scala> Some(1) orElse None
res0: Option[Int] = Some(1)
scala> None orElse Some(1)
res1: Option[Int] = Some(1)
scala> None orElse None
res2: Option[Nothing] = None
scala> Some(1) orElse Some(2)
res3: Option[Int] = Some(1)
It's not hard to do it by hand:
scala> val a = Some(1)
a: Some[Int] = Some(1)
scala> val b = Some(2)
b: Some[Int] = Some(2)
scala> Seq(a,b).flatten.headOption
res0: Option[Int] = Some(1)
In the question comments, you mention you can't have Some(a) and Some(b), so what you really have is Option[Either[Int,Int]]. In that case, you can use x.map(_.merge) to get back to Option[Int], eg
scala> val x:Option[Either[Int,Int]] = Some(Left(2))
x: Option[Either[Int,Int]] = Some(Left(2))
scala> x.map(_.merge)
res0: Option[Int] = Some(2)
What is the equivalent of Haskell's sequence in Scala? I want to turn list of options into an option of list. It should come out as None if any of the options is None.
List(Some(1), None, Some(2)).??? --> None
List(Some(1), Some(2), Some(3)).??? --> Some(List(1, 2, 3))
Scalaz defines sequence.
Here's an example:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> List(Some(1), None, Some(2)).sequence
res0: Option[List[Int]] = None
scala> List(some(1), some(2), some(3)).sequence
res1: Option[List[Int]] = Some(List(1, 2, 3))
Note that in the second example, you have to use Scalaz's some function to create a Some -- otherwise, the List is constructed as List[Some[Int]], which results in this error:
scala> List(Some(1), Some(2), Some(3)).sequence
<console>:14: error: could not find implicit value for parameter n: scalaz.Applicative[N]
List(Some(1), Some(2), Some(3)).sequence
The Scalaz some(a) and none functions create Some and None values of type Option[A].
If you want a solution for just List and Option rather a general monad then following will do the job,
def sequence[T](l : List[Option[T]]) =
if (l.contains(None)) None else Some(l.flatten)
REPL session,
scala> sequence(List(Some(1), None, Some(2)))
res2: Option[List[Int]] = None
scala> sequence(List(Some(1), Some(2), Some(3)))
res3: Option[List[Int]] = Some(List(1, 2, 3))
Update 20/8/2014
Just use Scalaz ...
Here is the same function as above using a combination of foldRight and map/ flatmap that only has to traverse the list once:
def sequence[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
ol flatMap (l => opt map (o => o::l))
}
Or, if you prefer the for comprehension version:
def sequence2[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
for {l <- ol; o <- opt} yield (o::l)
}
First off, I recommend that you check out the API docs for List.
As for a solution, this may not be the most graceful way to do it, but it'll work (and with no external dependencies):
// a function that checks if an option is a None
def isNone(opt:Option[_]) = opt match {
case None => true
case _ => false
}
//templated for type T so you can use whatever Options
def optionifyList[T](list:List[Option[T]]) = list.exists(isNone) match {
case true => None
case false => Some(list.flatten)
}
And a test just to be sure...
scala> val hasNone = Some(1) :: None :: Some(2) :: Nil
hasNone: List[Option[Int]] = List(Some(1), None, Some(2))
scala> val hasSome = Some(1) :: Some(2) :: Some(3) :: Nil
hasSome: List[Some[Int]] = List(Some(1), Some(2), Some(3))
scala> optionifyList(hasSome)
res2: Option[List[Int]] = Some(List(1, 2, 3))
scala> optionifyList(hasNone)
res3: Option[List[Int]] = None
Maybe this helps, as it traverses once only and use recursion
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a match {
case Nil => Some(Nil)
case h :: rest => h.flatMap(x => sequence(rest).map(x :: _))
}
This is very simple with a for comprehension:
val x : Option[String] = Option("x")
val y : Option[String] = Option("y")
val z : Option[String] = None
// Result -> a: Option[List[String]] = None
val a = for {
x <- x
y <- y
z <- z
} yield List(x,y,z)
// Result -> b: Option[List[String]] = Some(List(x, y))
val b = for {
x <- x
y <- y
} yield List(x,y)
Since you need to flatten anyway, just do it first...
def sequence(lo: List[Option[A]]): Option[List[A]] = lo.flatten match {
la: List[A] if(la.length == lo.length) => Some(la)
_ => None
}
tail recursion might be quickest