46

I am trying to get a number out of an xml field

...
<Quantity>12</Quantity>
...

via

Some((recipe \ "Main" \ "Quantity").text.toInt)

Sometimes there may not be a value in the xml, though. The text will be "" and this throws an java.lang.NumberFormatException.

What is a clean way to get either an Int or a None?

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
doub1ejack
  • 10,627
  • 20
  • 66
  • 125
  • Possible duplicate of [Scala is a string parseable as a double](http://stackoverflow.com/questions/9542126/scala-is-a-string-parseable-as-a-double) – thSoft Feb 14 '17 at 15:08

4 Answers4

80
scala> import scala.util.Try
import scala.util.Try

scala> def tryToInt( s: String ) = Try(s.toInt).toOption
tryToInt: (s: String)Option[Int]

scala> tryToInt("123")
res0: Option[Int] = Some(123)

scala> tryToInt("")
res1: Option[Int] = None
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
  • 8
    If you are parsing a lot of empty strings, you might want to catch them explicitly instead of throwing an exception every time: `if (s.isEmpty) None else Try(s.toInt).toOption`. – Rex Kerr May 22 '14 at 16:37
31

Scala 2.13 introduced String::toIntOption:

"5".toIntOption                 // Option[Int] = Some(5)
"abc".toIntOption               // Option[Int] = None
"abc".toIntOption.getOrElse(-1) // Int = -1
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
11

More of a side note on usage following accepted answer. After import scala.util.Try, consider

implicit class RichOptionConvert(val s: String) extends AnyVal {
  def toOptInt() = Try (s.toInt) toOption
}

or similarly but in a bit more elaborated form that addresses only the relevant exception in converting onto integral values, after import java.lang.NumberFormatException,

implicit class RichOptionConvert(val s: String) extends AnyVal {
  def toOptInt() = 
    try { 
      Some(s.toInt) 
    } catch { 
      case e: NumberFormatException => None 
    }
}

Thus,

"123".toOptInt
res: Option[Int] = Some(123)

Array(4,5,6).mkString.toOptInt
res: Option[Int] = Some(456)

"nan".toInt
res: Option[Int] = None
elm
  • 20,117
  • 14
  • 67
  • 113
5

Here's another way of doing this that doesn't require writing your own function and which can also be used to lift to Either.

scala> import util.control.Exception._
import util.control.Exception._

scala> allCatch.opt { "42".toInt }
res0: Option[Int] = Some(42)

scala> allCatch.opt { "answer".toInt }
res1: Option[Int] = None

scala> allCatch.either { "42".toInt }
res3: scala.util.Either[Throwable,Int] = Right(42)

(A nice blog post on the subject.)

Caoilte
  • 2,381
  • 1
  • 21
  • 27