41

Say I have a val s: Option[Option[String]]. It can thus have the following values:

Some(Some("foo")) Some(None) None

I want to reduce it so that the first becomes Some("foo") while the two others become None. Obviously there are many ways to accomplish this, but I'm looking for a simple, perhaps built-in, less-than-one-liner.

philipxy
  • 14,867
  • 6
  • 39
  • 83
Knut Arne Vedaa
  • 15,372
  • 11
  • 48
  • 59

6 Answers6

38

It's a shame that flatten doesn't exist. It should.

Flatten does exist now.

As before,

s getOrElse None

(in addition to the other answers) will also do the same thing.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 5
    seems to exist now: scala> Some(Some(1)).flatten res10: Option[Int] = Some(1) – Alexy Apr 14 '14 at 20:43
  • it seems [flatten has made it into cats as well](https://non.github.io/cats//tut/monad.html) – StuartLC Jan 20 '16 at 16:21
  • @Alexy when I call `flatten` on an object like `Some(Some(Some(1)))` I get `Cannot prove that Any <:< Option[B]` – bachr Sep 02 '16 at 16:44
16

You could use scalaz join to do this, as this is one of the monadic operations:

doubleOpt.join

Here it is in the REPL:

scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._

scala> some(some("X")).join
res0: Option[java.lang.String] = Some(X)

scala> some(none[String]).join
res1: Option[String] = None

scala> none[Option[String]].join
res3: Option[String] = None

It's available to anything with a typeclass instance for a Monad.

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • I've a map like `val map = Map("a"->Some(Some(1)), "b"->2)`, when I call `map.get("a").join` I see `could not find implicit value for parameter ev: scalaz.Liskov.<~<[Any,Option[B]]` – bachr Sep 02 '16 at 16:49
14
s.flatten

followed by a bunch of characters to get me up to the minimum that stackoverflow allows

Dave Griffith
  • 20,435
  • 3
  • 55
  • 76
4

I think the conversion to the Iterable is just fine. Use these steps to go from Option[Option[String] to a single Option[String]

s.flatten.headOption 

(which returns Option[String])

Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180
Dean Hiller
  • 19,235
  • 25
  • 129
  • 212
1

You might use flatMap like the following:

val options = List(Some(Some(1)), Some(None), None)
options map (_ flatMap (a => a))

This will map the List[Option[Option[Int]]] to a List[Option[Int]].
If you just have an Option you can use it as following:

val option = Some(Some(2))
val unzippedOption = option flatMap (b => b)

This will flatten your Option[Option[Int]] to Option[Int].

tgr
  • 3,557
  • 4
  • 33
  • 63
-4

Well, I actually don't understand how come it could be just None (the third case). If it can really be also just None, then I would vote for Rex Kerr's answer, otherwise just .get would be enough:

scala> Some(Some("foo")).get
res0: Some[java.lang.String] = Some(foo)

scala> Some(None).get
res1: None.type = None
Antonin Brettsnajdr
  • 4,073
  • 2
  • 20
  • 14
  • It can be `None` because it's an `Option`! – oxbow_lakes May 11 '11 at 22:51
  • Yeah, but this is Option inside Option ... so I would expect it's Some(Some("something")) for positive result and Some(None) for negative result. What third state describes just None then? Well if the problem is in 3-state logic, only then it makes sense. – Antonin Brettsnajdr May 12 '11 at 06:38
  • 3
    It's kind of equivalent to a future: `Some(Some(X))` is a calculated value, `Some(None)` indicates the future has finished with no value and `None` indicates there future has not returned – oxbow_lakes May 12 '11 at 09:34
  • 1
    Option[String] is a String or not. Let's write that as String + 1, meaning it can be any String or 1 other thing. An Option[Option[String]] then is (String + 1) + 1, or String + 2. Which is to say it's a String or it's one of two other things. In other words Option[Option[String]] is isomorphic to Either[Boolean, String]. I think the later structure indicates more clearly that it either the computation is successful in producing a string or may fail in two different ways. – James Iry May 12 '11 at 21:16
  • 1
    Hehe, nice explanation -- "There are many ways to fail, but only one to succeed." :-) – Antonin Brettsnajdr May 13 '11 at 06:57