2013-02-27 9 views
7

私はモデルを持っていますが、これにはOptionフィールドがあり、これには別のOptionフィールドが含まれています。たとえば:Scala 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を探していましたが、それを行うためのより良い方法を見つけられませんでした。

アイデア? ありがとうございます。

+2

だけ音符が、flatMapのwoun」:ここ

は、REPLの一部の図(4つのスタイルは最初の二つは、flatMapの平らなチェーンを使用して、ネストflatMap、他の二つを用いて、等価である)であります以下のように数回働く。 'First.second.flatMap(_。third.flatMap(_。numberOfSmth))。get'でなければならず、まだ例外がスローされている可能性があります。 – korefn

+0

本当にありがとうございます。皆さん、お返事ありがとうございました。私が探していたものを見つけました。 – psisoyev

答えて

14

溶液をOption.mapOption.flatMap使用することである:これは、Option[Int]を返し

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

(提供:

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

または同等(この回答の末尾に更新を参照されたいです)そのnumberOfSmthIntを返します)。コールチェーン内のオプションのいずれかがNoneの場合、結果はNoneになります。そうでない場合はSome(count)になります。ここで、countnumberOfSmthの戻り値です。

もちろん、これは非常に醜いことができます。この理由のために、scalaは理解のためにをサポートします。は構文的な砂糖です。

(確かにスカラを使用してしばらくする場合のように、あなたはまだ、どこでも map/ flatMap見ることに使用されていない場合は特に)間違いなく進歩して、正確なを生成
for { 
    first <- First 
    second <- first .second 
    third <- second.third 
} third.numberOfSmth 

:上記のように書き換えることができますフードの下に同じコード。 What is Scala's yield?

UPDATE:flatMapが連想であることを指摘してベン・ジェームズに おかげ

詳細な背景については、この他の質問を確認することができます。つまり、x flatMap(y flatMap z)))x flatMap y flatMap zと同じです。後者は通常短くはありませんが、それは従うのが簡単な入れ子を避けるという利点があります。

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) 
+3

あなたは醜いネスティングを使う必要はありません: 'flatMap'、' a flatMap(b flatMap c) 'の連想性は' a flatmap b flatMap c'と同じです。 –

+0

ありがとう、あなたは非常に正しいです。私は通常、これらを入れ子にしています。これは、実際にmappped/flatMappedの構造を模倣しているためです(この場合**は**ネストされています)。しかし、フラット・マップのフラット・チェーンとして読みやすくなっているのは事実です。 –

+1

flatMapsを入れ子にしないことはネストされたラムダ式でラムダパラメータを使用できないという欠点があることに注意してください。この場合でも動作しますが一般的には推奨しません。また、より遅く、より多くの関数オブジェクトを作成します。 –

4

これはflatMapへのコールを連鎖して行うことができます。

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

あなたはまたのために、理解してこれを行うことができますが、それはそれぞれの中間値に名前を付けるためにあなたを強制されることがより冗長です:

def getN(first: Option[First]): Option[Int] = 
    for { 
    f <- first 
    s <- f.second 
    t <- s.third 
    n <- t.numberOfSmth 
    } yield n 

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

あなたがC:scalazの必要はありません

10

使用ネストされたflatMaps

0

私はそれがあなたの問題のためにやり過ぎだと思うが、単に一般的なリファレンスとして:

このネストされたアクセスの問題は、レンズと呼ばれる概念によって対処されます。それらは、単純な構成でネストされたデータ型にアクセスする素敵なメカニズムを提供します。はじめに、this SO answerまたはthis tutorialを確認してください。あなたのケースでレンズを使用することが理にかなっているかどうかは、ネストされたオプション構造(注:アップデートではなく、変更可能でない新しいインスタンスを返す)で多くの更新を行う必要があるかどうかです。レンズなしでは、これは長いネストされたケースクラスcopyコードにつながります。まったく更新する必要がなければ、私はom-nom-nom's suggestionに固執します。

+2

@Downvoter:downvoteについて説明してもらえますか? OPがこのネストされた構造を多く更新しなければならないことを考えると、私はOPを代替ソリューションとしてのレンズの概念に向けるべきだと思いますか? – bluenote10