Skip to content

Instantly share code, notes, and snippets.

@kyrsideris
Created January 31, 2021 20:13
Show Gist options
  • Save kyrsideris/ceccce60d2a7002d6e15184579c563ed to your computer and use it in GitHub Desktop.
Save kyrsideris/ceccce60d2a7002d6e15184579c563ed to your computer and use it in GitHub Desktop.
// How to work with Options of Options of Sequences in Scala
type X = String
case class A(osx: Option[Seq[X]])
case class B(oa: Option[A])
val b = B( Some(A( Some(Seq("a1", "a2", "a3")) )) )
// Task: given b, get the Seq[X] from A.osx
var xs: Seq[X] = ???
// What to know:
Option(B).toSeq == Seq[B.type] // Option is like a list that might contain 1 or 0 elements
// Explanation:
// Option[B] {
// def toSeq: Seq[B] = {
// this match {
// case Some(b: B) => Seq[B](b)
// case None => Seq()
// }
// }
// }
// Approach 1
// Work with Options through B to A and then getOrElse
xs = b.oa.flatMap(_.osx).flatMap(sx => Some(sx)).getOrElse(Nil) // or
xs = b.oa.flatMap(_.osx).flatMap(Some(_)).getOrElse(Nil)
// with types written down
xs = b // B
.oa // Option[A]
.flatMap[Seq[X]]( // Option[A].flatMap[Seq[X]]
a => a.osx // A => Option[Seq[X]]
) // Option[Seq[X]]
.flatMap[Seq[X]]( // Option[Seq[X]].flatMap[Seq[X]]
sx => Some(sx) // Seq[X] => Some[Seq[X]]
) // Option[Seq[X]]
.getOrElse[Seq[X]]( // Option[Seq[X]].getOrElse[Seq[X]]
Nil // default :=> Seq[X]
) // Seq[X]
// Learning point: This approach is easy to follow as it uses Options all the way but it is verbose
// Approach 2
// Convert B.oa Option to Sequence, then apply a function in flatMap to get the A.osx
xs = b.oa.toSeq.flatMap(a => a.osx.toSeq.flatMap(sx => sx)) // or
xs = b.oa.toSeq.flatMap(a => a.osx.toSeq.flatten) // or
xs = b.oa.toSeq.flatMap(_.osx.toSeq.flatten)
// with types written down
xs = b // B
.oa // Option[A]
.toSeq // Seq[A]
.flatMap[X, Seq[X]]( // Seq[A].flatMap[X, Seq[X]]
a => // A => Seq[X]
a.osx // Option[Seq[X]]
.toSeq // Seq[Seq[X]]
.flatten[X] // Seq[Seq[X]].flatten[X]
) // Seq[X]
// Learning point: This approach is dealing only with Sequences
// but it complicates the conversion because it uses a deeper nested function
// Approach 3
// Work with Options to get A.osx, convert to Seq of Seq and finally flatten it
xs = b.oa.flatMap(a => a.osx).toSeq.flatten // or
xs = b.oa.flatMap(_.osx).toSeq.flatten
// with types written down
xs = b // B
.oa // Option[A]
.flatMap[Seq[X]]( // Option[A].flatMap[Seq[X]]
a => a.osx // A => Option[Seq[X]]
) // Option[Seq[X]]
.toSeq // Seq[Seq[X]]
.flatten // Seq[X]
// Learning point: This approach is simpler and less verbose because it is handling
// Options to get the required field, smaller function in flatMap and less complicated types.
// Conclusion: Use options to reach the field, smaller functions in flatMap,
// and then finally Option to Sequence along with flattening to get the result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment