另一个Option对象中的Scala Option对象 [英] Scala Option object inside another Option object

查看:78
本文介绍了另一个Option对象中的Scala Option对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个模型,其中包含一些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.mapOption.flatMap:

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

或同等功能(请参阅此答案末尾的更新):

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

这将返回一个Option[Int](前提是numberOfSmth返回一个Int).如果调用链中的任何选项为None,则结果将为None,否则结果将为Some(count),其中countnumberOfSmth返回的值.

当然,这很快就会变得丑陋.因此,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆