Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Option object inside another Option object

I have a model, which has some Option fields, which contain another Option fields. For example:

case class First(second: Option[Second], name: Option[String])
case class Second(third: Option[Third], title: Option[String])
case class Third(numberOfSmth: Option[Int])

I'm receiving this data from external JSON's and sometimes this data may contain null's, that was the reason of such model design.

So the question is: what is the best way to get a deepest field?

First.get.second.get.third.get.numberOfSmth.get

Above method looks really ugly and it may cause exception if one of the objects will be None. I was looking in to Scalaz lib, but didn't figure out a better way to do that.

Any ideas? Thanks in advance.

like image 516
psisoyev Avatar asked Feb 27 '13 11:02

psisoyev


People also ask

What are options in Scala?

Scala - Options. Scala Option[ T ] is a container for zero or one element of a given type. An Option[T] can be either Some[T] or None object, which represents a missing value. For instance, the get method of Scala's Map produces Some(value) if a value corresponding to a given key has been found, or None if the given key is not defined in the Map.

What is the use of some and none in Scala?

This some and none are part of Scala Option that searches for the key and returns the value if it founds that and none is returned if it doesn’t find the value. So here it uses the Scala Option to fetch the value and handle none. It can be best use when we have to implement the Scala Pattern Matching.

What is everything in Oops in Scala?

Everything is an object in OOPs. Object-oriented programing language based on classes and objects and they implement real-world scenarios. Scala programming language creates many objects and they can be created by using constructor or inside any method.

What is the use of apply method in companion object in Scala?

The apply method in the companion object acts as a Factory Method, and Scala’s syntactic sugar lets you use the syntax shown, creating new class instances without using the new keyword. To demonstrate how this feature works, here’s a class named Person along with an apply method in its companion object:


1 Answers

The solution is to use Option.map and Option.flatMap:

First.flatMap(_.second.flatMap(_.third.map(_.numberOfSmth)))

Or the equivalent (see the update at the end of this answer):

First flatMap(_.second) flatMap(_.third) map(_.numberOfSmth)

This returns an Option[Int] (provided that numberOfSmth returns an Int). If any of the options in the call chain is None, the result will be None, otherwise it will be Some(count) where count is the value returned by numberOfSmth.

Of course this can get ugly very fast. For this reason scala supports for comprehensions as a syntactic sugar. The above can be rewritten as:

for { 
  first <- First
  second <- first .second
  third <- second.third
} third.numberOfSmth

Which is arguably nicer (especially if you are not yet used to seeing map/flatMap everywhere, as will certainly be the case after a while using scala), and generates the exact same code under the hood.

For more background, you may check this other question: What is Scala's yield?

UPDATE: Thanks to Ben James for pointing out that flatMap is associative. In other words x flatMap(y flatMap z))) is the same as x flatMap y flatMap z. While the latter is usually not shorter, it has the advantage of avoiding any nesting, which is easier to follow.

Here is some illustration in the REPL (the 4 styles are equivalent, with the first two using flatMap nesting, the other two using flat chains of flatMap):

scala> val l = Some(1,Some(2,Some(3,"aze")))
l: Some[(Int, Some[(Int, Some[(Int, String)])])] = Some((1,Some((2,Some((3,aze))))))
scala> l.flatMap(_._2.flatMap(_._2.map(_._2)))
res22: Option[String] = Some(aze)
scala> l flatMap(_._2 flatMap(_._2 map(_._2)))
res23: Option[String] = Some(aze)
scala> l flatMap(_._2) flatMap(_._2) map(_._2)
res24: Option[String] = Some(aze)
scala> l.flatMap(_._2).flatMap(_._2).map(_._2)
res25: Option[String] = Some(aze)
like image 136
Régis Jean-Gilles Avatar answered Sep 18 '22 17:09

Régis Jean-Gilles