另一个Option对象中的Scala Option对象 [英] Scala Option object inside another Option object
问题描述
我有一个模型,其中包含一些Option字段,其中包含另一个Option字段.例如:
case class First(second: Option[Second], name: Option[String])
case class Second(third: Option[Third], title: Option[String])
case class Third(numberOfSmth: Option[Int])
我正在从外部JSON接收此数据,有时该数据可能包含null,这就是这种模型设计的原因.
问题是:进入最深领域的最佳方法是什么?
First.get.second.get.third.get.numberOfSmth.get
上述方法看起来非常丑陋,如果其中一个对象为None,则可能导致异常.我一直在寻找Scalaz lib,但没有找到一种更好的方法来做到这一点.
有什么想法吗? 预先感谢.
解决方案是使用Option.map
和Option.flatMap
:
First.flatMap(_.second.flatMap(_.third.map(_.numberOfSmth)))
或同等功能(请参阅此答案末尾的更新):
First flatMap(_.second) flatMap(_.third) map(_.numberOfSmth)
这将返回一个Option[Int]
(前提是numberOfSmth
返回一个Int
).如果调用链中的任何选项为None
,则结果将为None
,否则结果将为Some(count)
,其中count
是numberOfSmth
返回的值.
当然,这很快就会变得丑陋.因此,scala支持理解作为语法糖.上面可以改写为:
for {
first <- First
second <- first .second
third <- second.third
} third.numberOfSmth
这可以说是更好的(特别是如果您还不习惯在任何地方看到map
/flatMap
,这肯定会在使用scala之后的情况下出现),并且在幕后生成完全相同的代码. /p>
有关更多背景信息,您可以检查以下其他问题: Scala的产量是多少?
更新:
感谢Ben James指出flatMap是关联的.换句话说,x flatMap(y flatMap z)))
与x flatMap y flatMap z
相同.尽管后者通常并不短,但它具有避免嵌套的优点,而嵌套更易于遵循.
以下是REPL中的一些插图(这4种样式是等效的,前两种样式使用flatMap嵌套,另两种样式使用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)
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.
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)
这篇关于另一个Option对象中的Scala Option对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!