12

別のオプション フィールドを含むいくつかのオプション フィールドを持つモデルがあります。例えば:

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

上記のメソッドは見栄えが悪く、オブジェクトの 1 つが None になると例外が発生する可能性があります。私は Scalaz lib を調べていましたが、それを行うためのより良い方法がわかりませんでした。

何か案は?前もって感謝します。

4

4 に答える 4

20

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)countnumberOfSmth

もちろん、これは非常に速く醜くなる可能性があります。このため、scala はシンタックス シュガーとしての理解をサポートします。上記は次のように書き換えることができます。

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

mapこれは間違いなく優れており (特に、scala を使用してしばらくすると確実にそうなるように、どこでも/を見ることにまだ慣れていないflatMap場合)、内部でまったく同じコードを生成します。

詳細な背景については、この他の質問を確認してください: Scala の利回りは何ですか?

更新: flatMap が連想的であることを指摘してくれた Ben James に感謝します。つまりx flatMap(y flatMap z)))と同じx flatMap y flatMap zです。後者は通常短くはありませんが、ネストを回避できるという利点があり、従うのが簡単です。

REPL の図を次に示します (4 つのスタイルは同等で、最初の 2 つは flatMap ネストを使用し、他の 2 つは 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)
于 2013-02-27T12:05:58.183 に答える
10

scalaz は必要ありません:

for { 
  first  <- yourFirst
  second <- f.second
  third  <- second.third
  number <- third.numberOfSmth
} yield number

または、ネストされた flatMaps を使用できます

于 2013-02-27T12:03:37.810 に答える
4

これは、次の呼び出しを連鎖させることで実行できますflatMap

def getN(first: Option[First]): Option[Int] =
  first flatMap (_.second) flatMap (_.third) flatMap (_.numberOfSmth)

for-comprehension を使用してこれを行うこともできますが、各中間値に名前を付ける必要があるため、より冗長になります。

def getN(first: Option[First]): Option[Int] =
  for {
    f <- first
    s <- f.second
    t <- s.third
    n <- t.numberOfSmth
  } yield n
于 2013-02-27T12:03:23.273 に答える
1

あなたの問題にはやり過ぎだと思いますが、一般的な参考として:

このネストされたアクセスの問題は、レンズと呼ばれる概念によって対処されます。これらは、単純な構成によってネストされたデータ型にアクセスする優れたメカニズムを提供します。導入として、たとえば、この SO 回答またはこのチュートリアルを確認することをお勧めします。あなたのケースでレンズを使用することが理にかなっているのかどうかという問題は、ネストされたオプション構造で多くの更新を実行する必要があるかどうかです (注:変更可能な意味での更新ではなく、変更されたが変更された新しいインスタンスを返します)。レンズがないと、ネストされたケース クラスcopyコードが長くなります。まったく更新する必要がない場合は、om-nom-nom の提案に従います。

于 2013-02-27T12:04:27.860 に答える