J  Calbreath
J Calbreath

Reputation: 2715

Scala: Succint way to check for null/empty values and return 0 instead

I need to convert some string values to Ints after reading in a data file. It's possible the field will be empty, which will cause an error when it tries to do the conversion. Is there an easy way to check for null (or an appropriate value that can be converted to integer) and just return 0 if it can't be converted? getOrElse seemed to be a possibility but I can't quite get that to work write. I could just write a function that could do it pretty easily but I was hoping there was a more elegant/succinct way to do this.

Upvotes: 2

Views: 6448

Answers (3)

Ben Reich
Ben Reich

Reputation: 16324

You can use the Try monad (documented here), which has a getOrElse method:

import scala.util.Try
val zero = Try { "foo".toInt }.getOrElse(0)
val leet = Try { "1337".toInt }.getOrElse(0)

val parseOrZero: String => Int = x => Try(x.toInt) getOrElse 0
val lines = Seq("123", "42", "sdfd", null, "")
lines.map(parseOrZero)
->> res0: Seq[Int] = List(123, 42, 0, 0, 0)

Or in-line:

lines.map(x => Try(x.toInt) getOrElse 0)

Try also has a toOption method, which gets Some(value) if successful, or None on exception. So, if you wanted to filter out the bad entries instead of returning 0, you could do:

lines.flatMap(x => Try(x.toInt).toOption)

Try also has all the useful monadic transformations like map and flatMap for chaining more computation.

Upvotes: 9

Sergii Lagutin
Sergii Lagutin

Reputation: 10681

Convert only integer value:

val lines = Seq("123", "42", "sdfd", null, "")  // read strings from anywhere

val result = lines
  .filter(s => Option(s).isDefined)   // drop null values
  .filter(_.matches("\\d+"))          // drop non integer values
  .map(Integer.parseInt)              // convert to int

result: Seq[Int] = List(123, 42)

Or return 0 instead of incorrect:

val lines = Seq("123", "42", "sdfd", null, "") // read strings from anywhere

def convert(s: String): Int =
  Option(s)
    .filter(_.matches("\\d+"))
    .map(Integer.parseInt)
    .getOrElse(0)

val result = lines.map(convert)

result: Seq[Int] = List(123, 42, 0, 0, 0)

Upvotes: 1

Gabriele Petronella
Gabriele Petronella

Reputation: 108169

The default toInt function throws an exception, but you can provide a safer version returning an Option and then use getOrElse on it

def toInt(s: String): Option[Int] =
  try {
    Some(s.toInt)
  } catch {
    case e: Exception => None
  }

toInt("42").getOrElse(0) // 42
toInt("foo").getOrElse(0) // 0

Alternatively you can use scalaz, which provides a parseInt method on String which returns a Validation[NumberFormatException, Int], which you can turn into an Option. Example:

"42".parseInt.toOption.getOrElse(0) // 42
"foo".parseInt.toOption.getOrElse(0) // 0

Upvotes: 1

Related Questions