Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Genaric type parsing in Scala with default value

Tags:

scala

I am looking for a Generic functional way to convert between Scala String to any numeric type. I need in case of failure to pass a default value.

For example, I need to convert from String to Int but in case the String to Int conversion failed. I need to pass a default value without having throws java.lang.NumberFormatException. I tried this way but doesn't get my idea as I need it generic and also with default value in case of exception

like image 994
Sa2012 Avatar asked Dec 17 '22 17:12

Sa2012


2 Answers

Edit: I updated the solution to parse from any type to any type. This makes the solution more generic based on the question requested. I think you can use Scala functional way to have the generic type [T] but you need to split it into two parts.

First to implement parse types which parses from any type [U] to any type [T]. parseTypes takes a function canBuildFrom as a parameter using Scala functional way. Then based on the output of this function you will checks if it parsed correctly or it has an exception. Also, in case it failed to parse you can pass a default parameter.

  def parseTypes[T,U](str: U, canBuildFrom: U ⇒ T): Either[java.lang.NumberFormatException, T] =
    Try(canBuildFrom(str)).map(Right(_)).getOrElse {
      Left(new java.lang.NumberFormatException(str.toString))
    }

  def safeParse[T,U](attributeValue: U, canBuildFrom: U ⇒ T, defaultValue: T): T = {
    parseTypes(attributeValue, canBuildFrom) match {
      case Right(x) ⇒ x
      case Left(x)  ⇒ defaultValue
      case _        ⇒ defaultValue
    }
  }


  def safeParseDoubleToBigDecimal(attributeValue: Double): BigDecimal = safeParse[BigDecimal,Double](attributeValue, toBigDecimal, 0.0)

You can use it to parse String to Int, Double, and Decimal as following:

  def safeParseStringToInt(attributeValue: String): Int = safeParse[Int,String](attributeValue, _.toInt, 0)

  def safeParseStringToDouble(attributeValue: String): Double = safeParse[Double ,String](attributeValue, _.toDouble, 0.0)

  def safeParseStringToBigDecimal(attributeValue: String): BigDecimal = safeParse[BigDecimal ,String](attributeValue, BigDecimal(_), 0.0)
  // example of usage
  val x:Int = safeParseStringToInt("123",0)
  val y:Int = safeParseStringToInt("aaa",0)

Update: I update this answer as I realized that @Dima's answer is somehow more functional and better than my answer I added the answer below copied from @Dima's answer as my answer marked as the correct answer.

 trait ParseIt[T] {
     protected def parse(s: String): T
     def apply(s: String) = Try(parse(s)).toOption
 }

 implicit object ParseInt extends ParseIt[Int] {
   protected def parse(s: String) = s.toInt
 }

 implicit object ParseDouble extends ParseIt[Double] {
   protected def parse(s: String) = s.toDouble
 }
 // etc ...

 def parse[T : ParseIt](s: String, orElse: => T) = 
   implicitly[ParseIt[T]](s).getOrElse(orElse)

 val n: Int = parse("123", 0)
 val d: Double = parse("123", 0.0)
like image 159
Moustafa Mahmoud Avatar answered Jan 09 '23 12:01

Moustafa Mahmoud


This sort of thing is implemented really nice with typeclasses:

 trait ParseIt[T] {
     protected def parse(s: String): T
     def apply(s: String) = Try(parse(s)).toOption
 }

 implicit object ParseInt extends ParseIt[Int] {
   protected def parse(s: String) = s.toInt
 }

 implicit object ParseDouble extends ParseIt[Double] {
   protected def parse(s: String) = s.toDouble
 }
 // etc ...

 def parse[T : ParseIt](s: String, orElse: => T) = 
   implicitly[ParseIt[T]](s).getOrElse(orElse)

 val n: Int = parse("123", 0)
 val d: Double = parse("123", 0.0)
like image 23
Dima Avatar answered Jan 09 '23 12:01

Dima