BBH
-Biz Branding Hub-
投稿日 : 
2020/10/19
更新日 : 
2020/10/19

【Scala】foreachやmapを使ってOptionに値がある時のみ処理を実行する

ScalaでOption値を扱う場合、foreachやmapを使うことで、値が存在するときのみ処理を実行することができます。

戻り値が不要な場合はforeachを使用する

戻り値が不要な場合はforeachを使用することで安全にOption型から値を取り出せます。

Optionに対しforeachで処理を行うサンプル

def main(arts: Array[String]) : Unit = {
    Option(1).foreach(println)  // =>1
    None.foreach(println)       // 何も表示しない(処理されない)
}

foreachと言うとListなどを逐次的に処理するイメージがありますが、ScalaのOption型においては関係ありません。
実装も以下のように値があれば、引数として渡された関数を実行するだけになっています。

Option.scala foreachの実装

@inline final def foreach[U](f: A => U): Unit = {
  if (!isEmpty) f(this.get)
}

戻り値が必要な時はmapメソッドを使う

戻り値が必要な時は、mapメソッドを使用します。
こちらもforeachと同様に、値がある時は処理し、なければNoneを返します。

Scala mapのサンプル

def main(args: Array[String]) : Unit = {
    val optionInt = Option(1)
    val optionNoneInt : Option[Int] = None
    val res1 = optionInt.map(calc)
    val res2 = optionNoneInt.map(calc)
    
    println(res1)
    println(res2)
}

def calc(i : Int) : Int = {
    println("=== 計算実施 ===")
    println(s"i = ${i}")
    i * 2
}

// 【出力結果】
// === 計算実施 ===
// i = 1
// Some(2)
// None

値がある時は計算が行われ、無い場合はNoneになっていることがわかります。
ちなみにmapの実装は以下のようになっていました。

Option.scala mapの実装
@inline final def map[B](f: A => B): Option[B] =
  if (isEmpty) None else Some(f(this.get))


関数がOption型を返す場合は、flatMapを使う

関数がOption型を返す場合、mapだとOption[Option[T]]のように、Optionが入れ子になってしまいます。

def main(args: Array[String]) : Unit = {
    val optionInt = Option(1)
    val res1 = optionInt.map(calc)
    println(res1)  // Some(Some(2))
}

def calc(i : Int) : Option[Int] = {
    Option(i * 2)
}

その場合はflatMapを使用すれば入れ子になることを防げます。

def main(args: Array[String]) : Unit = {
    val optionInt = Option(1)
    val res1 = optionInt.flatMap(calc)
    println(res1)  // Some(2)
}

以下、flatMapの実装です。
optionでくるむかどうかがmapとの違いになっています。

Option.scala

@inline final def flatMap[B](f: A => Option[B]): Option[B] =
  if (isEmpty) None else f(this.get)


matchを使う

今まで紹介したものはmatchを使用して同じことができます。

Scala optionをmatchで扱う

def main(args: Array[String]) : Unit = {
    val optionInt = Option(1)
    // foreachの書き換え
    optionInt match {
        case Some(v) => println(v)
        case None =>
    }
    
    // mapの書き換え
    val ans = optionInt match {
        case Some(v) => v * 2
        case None => 2
    }
}

Profile

管理人プロフィール

都内でITエンジニアをやってます。
変遷:中規模SES→独立系SIer→Webサービス内製開発
使用技術はその時々でバラバラですが、C#、AWSが長いです。
どちらかと言うとバックエンドより開発が多かったです。
顧客との折衝や要件定義、マネジメント(10名弱程度)の経験あり。
最近はJava+SpringBootがメイン。

Recommend