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
Related
Given a List[Any], I want to convert it to a Option[List[String]]
def convert(ls: List[Any]) : Option[List[String]] = {
if (ls.forall(_.getClass == classOf[String]))
Some(ls.map(_.asInstanceOf[String]))
else
None
}
Is there a better way ?
Like:
scala> val bag = List("a", 1, 2.0, "b")
bag: List[Any] = List(a, 1, 2.0, b)
scala> def unbagged(vs: List[Any]): Option[List[String]] = Option(vs collect { case s: String => s}) filter (_.nonEmpty)
unbagged: (vs: List[Any])Option[List[String]]
scala> unbagged(bag)
res0: Option[List[String]] = Some(List(a, b))
scala> unbagged(List(1, 3.14))
res1: Option[List[String]] = None
or, addressing the use case:
scala> def strung(vs: List[Any]): Option[List[String]] = (Option(vs) filter (_ forall { case _: String => true case _ => false })).asInstanceOf[Option[List[String]]]
strung: (vs: List[Any])Option[List[String]]
scala> strung(bag)
res3: Option[List[String]] = None
scala> strung(List("a","b","c"))
res4: Option[List[String]] = Some(List(a, b, c))
There are already quite a few answers, but I think they're all cleverer than needed. The initial proposal in the question is not that bad, except I would replace the getClass test by isInstanceOf:
def convert(ls: List[Any]): Option[List[String]] = {
if (ls.forall(_.isInstanceOf[String]))
Some(ls.map(_.asInstanceOf[String]))
else
None
}
It's functional, copies the list only once. Yes, the list is traversed twice, but typically that it still going to be faster than throwing an exception (which is usually slow -- if you really want to go that route, at least use a ControlThrowable, which does not record the stack trace when constructed).
Besides, as #som-snytt pointed out quietly in a comment, due to erasure, you don't even need to cast all the elements inside the list. You may just as well cast the list, which, after having checked that all elements are Strings, is just as safe as any other casts:
def convert(ls: List[Any]): Option[List[String]] = {
if (ls.forall(_.isInstanceOf[String]))
Some(ls.asInstanceOf[List[String]])
else
None
}
This is simply the most efficient version, because there is no list copying at all.
There is a toString method, which can make a String from any object. So if it's not a requirement that all elements of your original List should actually be String elements, you can do this:
import scala.util.Try
def convert(l: List[Any]) : Option[List[String]] = {
Try(l.map(_.toString)).toOption
}
Try will return Some(x) if it succeeds and obtains a value x, or None otherwise.
If the conversion should succeed only if all elements are Strings, then we can do the conversion inside Try (at the first failure, Try will fail and hence we'll get None)
import scala.util.Try
def convert(l: List[Any]) : Option[List[String]] = {
Try(l.map(_.asInstanceOf[String])).toOption
}
I would recommend to use pattern matching:
def convert(l: List[Any]) : Option[List[String]] = {
Try(list.collect{
case s : String => s
case x : Any => throw new Exception()
}).toOption
}
Code is a bit ugly but it works.
It doesnt use classOf but it uses pattern matching:
scala> val l1 = List("a", 1, 12.0)
l1: List[Any] = List(a, 1, 12.0)
scala> val l2 = List[Any]("a", "b", "c")
l2: List[Any] = List(a, b, c)
scala> def convert(list: List[Any]) = {
| list.foldLeft(Some(List()): Option[List[String]]) { (x, y) =>
| x match {
| case Some(l) =>
| y match {
| case elem: String => Some(l ::: List(elem))
| case _ => None
| }
| case None => None
| }
| }
| }
convert: (list: List[Any])Option[List[String]]
scala> convert(l1)
res12: Option[List[String]] = None
scala> convert(l2)
res13: Option[List[String]] = Some(List(a, b, c))
scala>
There is a straightforward solution using scalaz :
def convert(ls: List[Any]) : Option[List[String]] =
ls.map { a => if (a.isInstanceOf[String]) Some(a.asInstanceOf[String]) else None}.sequence
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
}
I have a List[String] that com.github.seratch.scalikesolr is populating a little inconveniantly...
If List has one element, it looks like this:
[value]
(It has prepended and appended brackets).
If List has more than 1 element, the elements look like:
[value1
value2]
Struggling to find clean looking scala-ish code to remove the brackets if present.
what do you suggest. thanks in advance.
P.S. I have several Lists to apply this "filter" to... so reusable code is the better approach.
def deBracketize(list: List[String]): List[String] = list.map(_.stripPrefix("[").stripSuffix("]"))
Example usage:
println(deBracketize(List("[value1", "value2]")))
println(deBracketize(List("[value1]")))
Outputs:
List(value1, value2)
List(value1)
How about this?
def removeBrackets(list: List[String]): List[String] = list.size match {
case 0|1 => list.map(_.stripPrefix("[").stripSuffix("]"))
case _ => (list.head.stripPrefix("[") :: list.drop(1).dropRight(1)) :+ list.last.stripSuffix("]")
}
Here in a functional style using an optional parameter.
def removeBracket(list: List[String], first: Int=1): List[String] =
list match {
case Nil => Nil
case a::Nil => List(a.substring(first,a.length-1))
case a::q => a.substring(first,a.length)::removeBracket(q,0)
}
Test:
scala> removeBracket(Nil)
res1: List[String] = Nil
scala> removeBracket(List("[test]"))
res1: List[String] = List("test")
scala> removeBracket(List("[test","tist]"))
res1: List[String] = List("test","tist")
scala> removeBracket(List("[test","toast","tist]"))
res1: List[String] = List("test","toast","tist")
How about this ?
$ scala
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :paste
// Entering paste mode (ctrl-D to finish)
def stripBrackets(str: String) = str.replaceAll("\\[", "").replaceAll("\\]", "")
def stripBrackets(ls: List[String]): List[String] =
ls match {
case Nil => ls
case List(head) => List(stripBrackets(head))
case List(head, last) => List(stripBrackets(head), stripBrackets(last))
case _ => stripBrackets(ls.head) :: ls.take(ls.size - 1).drop(1) ::: (stripBrackets(ls.last) :: Nil)
}
val singleElemList = List("[value1]")
val twoElemList = List("[value1", "value2]")
val multiElemList = List("[value1", "value2", "value3", "value4]")
// Exiting paste mode, now interpreting.
stripBrackets: (str: String)String <and> (ls: List[String])List[String]
stripBrackets: (str: String)String <and> (ls: List[String])List[String]
singleElemList: List[String] = List([value1])
twoElemList: List[String] = List([value1, value2])
multiElemList: List[String] = List([value1, value2, value3, value4])
scala> stripBrackets(singleElemList)
res0: List[String] = List(value1)
scala> stripBrackets(twoElemList)
res1: List[String] = List(value1, value2)
scala> stripBrackets(multiElemList)
res2: List[String] = List(value1, value2, value3, value4)
List("[value1", "value2]").map{_.replace("[","").replace("]","")}
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
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