I have a number of functions like this:
// Convert a string to integer, defaulting to 0 if it fails
def safeToInt(s: String): Int = try {
s.toInt
} catch {
case _: NumberFormatException => 0
}
// Convert a string to long, defaulting to 0 if it fails
def safeToLong(s: String): Long = try {
s.toLong
} catch {
case _: NumberFormatException => 0
}
// Convert a string to double, defaulting to 0 if it fails
def safeToDouble(s: String): Double = try {
s.toDouble
} catch {
case _: NumberFormatException => 0
}
Any way to make these cleaner? They essentially all do the same thing apart from one line.
You can take advantage of Numeric to avoid duplicating the zero.
import scala.util.Try
def safeToNumeric[A: Numeric](f: String => A)(s: String): A =
Try(f(s)).getOrElse(implicitly[Numeric[A]].zero)
val safeToInt = safeToNumeric(_.toInt)(_)
val safeToLong = safeToNumeric(_.toLong)(_)
val safeToDouble = safeToNumeric(_.toDouble)(_)
safeToInt("4") // 4
safeToDouble("a") // 0.0
Unfortunately Numeric doesn't give you the parsing method as well, but you can create the appropriate type class yourself ...
case class Parser[A](parse : String => A)
implicit val intParser = Parser(_.toInt)
implicit val longParser = Parser(_.toLong)
implicit val doubleParser = Parser(_.toDouble)
... and then you can write a single method that works for all of the types.
def safeTo[A: Parser : Numeric](s: String): A =
Try(implicitly[Parser[A]].parse(s))
.getOrElse(implicitly[Numeric[A]].zero)
safeTo[Int]("4") // 4
safeTo[Double]("a") // 0.0
You can use scala.util.Try
import util.Try
// Convert a string to integer, defaulting to 0 if it fails
def safeToInt(s: String): Int = Try(s.toInt).getOrElse(0)
// Convert a string to long, defaulting to 0 if it fails
def safeToLong(s: String): Long = Try(s.toLong).getOrElse(0L)
// Convert a string to double, defaulting to 0 if it fails
def safeToDouble(s: String): Double = Try(s.toDouble).getOrElse(0.0)
Although Chris Martin's answer is probably the most sophisticated, it is quite complex. You can encapsulate the "try something, or else get me the default" by using a function that returns a function.
import scala.util.Try
def safeConverter[A](convert: String => A, default: A) = { input: String =>
Try(convert(input)).getOrElse(default)
}
val safeToInt = safeConverter(_.toInt, 0)
val safeToLong = safeConverter(_.toLong, 0L)
val safeToDouble = safeConverter(_.toDouble, 0.0)
safeToInt("334") // Returns 334
safeToInt("this is not a number") // Returns 0
converter returns a function that performs conversion of a String with a default in case an exception is thrown.
You can use this converter for any conversion that potentially throws an exception, not just numeric values.
Related
Here is my attempt at writing a small parser for positive Ints:
import scala.util.parsing.combinator.RegexParsers
object PositiveIntParser extends RegexParsers {
private def positiveInt: Parser[Int] = """0*[1-9]\d*""".r ^^ { _.toInt }
def apply(input: String): Option[Int] = parseAll(positiveInt, input) match {
case Success(result, _) => Some(result)
case _ => None
}
}
The problem is that, if the input string is too long, toInt throws an NumberFormatException, which makes my parser blow up:
scala> :load PositiveIntParser.scala
Loading PositiveIntParser.scala...
import scala.util.parsing.combinator.RegexParsers
defined object PositiveIntParser
scala> PositiveIntParser("12")
res0: Option[Int] = Some(12)
scala> PositiveIntParser("-12")
res1: Option[Int] = None
scala> PositiveIntParser("123123123123123123")
java.lang.NumberFormatException: For input string: "123123123123123123"
at ...
Instead, I would like my positiveInt parser to fail gracefully (by returning a Failure) when toInt throws an exception. How can I do that?
An easy fix that comes to mind consists in limiting the length of the strings accepted by my regex, but that's unsatisfactory.
I'm guessing that a parser combinator for this use case is already provided by the scala.util.parsing.combinator library, but I've been unable to find one...
You can use a combinator accepting a partial function (inspired by how to make scala parser fail):
private def positiveInt: Parser[Int] = """0*[1-9]\d*""".r ^? {
case x if Try(x.toInt).isSuccess => x.toInt
}
If you want to avoid the double conversion, you can create an extractor to perform the matching and conversion:
object ParsedInt {
def unapply(str: String): Option[Int] = Try(str.toInt).toOption
}
private def positiveInt: Parser[Int] = """0*[1-9]\d*""".r ^? { case ParsedInt(x) => x }
It is also possible to move the positiveness test into the case condition, which I find more readable than a bit intricate regex:
private def positiveInt: Parser[Int] = """\d+""".r ^? { case ParsedInt(x) if x > 0 => x }
As per your comment the extraction can also be performed in a separate ^^ step, as follows:
private def positiveInt: Parser[Int] = """\d+""".r ^^
{ str => Try(str.toInt)} ^? { case util.Success(x) if x > 0 => x }
How about wrapping the call to parseAll with Try()?
Try(parseAll(positiveInt, input))
scala.util.Try's apply method will wrap any exception in Failure[T], and then you can even use .toOption to convert any Failure to a None.
Try(parseAll(positiveInt, input)).toOption
What's the best way to create the complement for an f-style string interpolation, i.e. a way to parse the arguments/numbers in a formatted string?
E.g.
val format = "frame%04d.png"
val i = 123
val out = f"frame$i%04d.png"
assert(out == "frame0123.png")
def parse(s: String, pat: String): Int = ???
val j = parse(out, format)
assert(j == 123)
I know I can manually construct a reg-exp, I just wonder if there is a general approach with f-style interpolated strings.
I was hoping for something simple like:
val f"frame$j%04d.png" = out
But
error: macro method f is not a case class, nor does it have an unapply/unapplySeq member
I found this nice post about using interpolators as pattern matchers so this is starting point for you:
object StringMatcher {
val ZeroFillDec = "%0(\\d+)d(.*)".r
val SimpleDec = "%(\\d+)d(.*)".r
val String = "%s(.*)".r
def patternize(part: String) = part match {
case ZeroFillDec(len, rest) => f"(\\d{${len.toInt}})$rest"
case SimpleDec(len, rest) => f"(\\d{1,${len.toInt}})$rest"
case String(rest) => f"(.*)$rest"
case rest => f"(.*)$rest"
}
}
implicit class StringMatcher(sc: StringContext) {
import StringMatcher.patternize
object pat {
def unapplySeq(s: String): Option[Seq[String]] = {
val re = (sc.parts.head ++ (sc.parts.tail map patternize)).mkString.r
re.unapplySeq(s)
}
}
}
Using that, code
val out = "frame0123_023.png"
val pat"frame$j%04d_$i%03d.png" = out
assigns
j: String = 0123
i: String = 023
What would be a fast a safe way to convert a String to a numeric type, while providing a default value when the conversion fails ?
I tried using the usually recommended way, i.e. using Exceptions:
implicit class StringConversion(val s: String) {
private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try {
convert(s)
} catch {
case _: NumberFormatException => defaultVal
}
def toShortOrElse(defaultVal: Short = 0) = toTypeOrElse[Short](_.toShort, defaultVal)
def toByteOrElse(defaultVal: Byte = 0) = toTypeOrElse[Byte](_.toByte, defaultVal)
def toIntOrElse(defaultVal: Int = 0) = toTypeOrElse[Int](_.toInt, defaultVal)
def toDoubleOrElse(defaultVal: Double = 0D) = toTypeOrElse[Double](_.toDouble, defaultVal)
def toLongOrElse(defaultVal: Long = 0L) = toTypeOrElse[Long](_.toLong, defaultVal)
def toFloatOrElse(defaultVal: Float = 0F) = toTypeOrElse[Float](_.toFloat, defaultVal)
}
Using this utility class, I can now easily convert any String to a given numeric type, and provide a default value in case the String is not representing correctly the numeric type:
scala> "123".toIntOrElse()
res1: Int = 123
scala> "abc".toIntOrElse(-1)
res2: Int = -1
scala> "abc".toIntOrElse()
res3: Int = 0
scala> "3.14159".toDoubleOrElse()
res4: Double = 3.14159
...
While it works beautifully, this approach does not seem to scale well, probably because of the Exceptions mechanism:
scala> for (i<-1 to 10000000) "1234".toIntOrElse()
takes roughly 1 second to execute whereas
scala> for (i<-1 to 10000000) "abcd".toIntOrElse()
takes roughly 1 minute!
I guess another approach would be to avoid relying on exceptions being triggered by the toInt, toDouble, ... methods.
Could this be achieved by checking if a String "is of the given type" ?
One could of course iterate through the String characters and check that they are digits (see e.g. this example), but then what about the other numeric formats (double, float, hex, octal, ...) ?
As a first approach, filter out those input strings that do not contain any digit
private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try {
if (s.contains("[0-9]")) convert(s) {
else defaultVal
} catch {
case _: NumberFormatException => defaultVal
}
}
Update
Enriched set of characters that may occur in a numeric value, yet no order of occurrence or limits in repetition considered,
private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try {
if (s matches "[\\+\\-0-9.e]+") convert(s)
else defaultVal
} catch {
case _: NumberFormatException => defaultVal
}
}
I am trying to cast a String to Int using extractors. My code looks as follows.
object Apply {
def unapply(s: String): Option[Int] = try {
Some(s.toInt)
} catch {
case _: java.lang.Exception => None
}
}
object App {
def toT[T](s: AnyRef): Option[T] = s match {
case v: T => Some(v)
case _ => None
}
def foo(param: String): Int = {
//reads a Map[String,String] m at runtime
toT[Int](m("offset")).getOrElse(0)
}
}
I get a runtime error: java.lang.String cannot be cast to java.lang.Integer. It seems the extractor is not being used at all. What should I do?
Edit: My use case is as follows. I am using play and I want to parse the query string passed in the url. I want to take the query string value (String) and use it as an Int, Double etc. For example,
val offset = getQueryStringAs[Int]("offset").getOrElse(0)
I think the biggest problem here is, that you seem to confuse casting and conversion. You have a Map[String, String] and therefore you can't cast the values to Int. You have to convert them. Luckily Scala adds the toInt method to strings through implicit conversion to StringOps.
This should work for you:
m("offset").toInt
Note that toInt will throw a java.lang.NumberFormatException if the string can not be converted to an integer.
edit:
What you want will afaik only work with typeclasses.
Here is an example:
trait StringConverter[A] {
def convert(x: String): A
}
implicit object StringToInt extends StringConverter[Int] {
def convert(x: String): Int = x.toInt
}
implicit object StringToDouble extends StringConverter[Double] {
def convert(x: String): Double = x.toDouble
}
implicit def string2StringConversion(x: String) = new {
def toT[A](implicit ev: StringConverter[A]) = ev.convert(x)
}
usage:
scala> "0.".toT[Double]
res6: Double = 0.0
There's a problem in your code, for which you should have received compiler warnings:
def toT[T](s: AnyRef): Option[T] = s match {
case v: T => Some(v) // this doesn't work, because T is erased
case _ => None
}
Now... where should Apply have been used? I see it declared, but I don't see it used anywhere.
EDIT
About the warning, look at the discussions around type erasure on Stack Overflow. For example, this answer I wrote on how to get around it -- though it's now deprecated with Scala 2.10.0.
To solve your problem I'd use type classes. For example:
abstract class Converter[T] {
def convert(s: String): T
}
object Converter {
def toConverter[T](converter: String => T): Converter[T] = new Converter[T] {
override def convert(s: String): T = converter(s)
}
implicit val intConverter = toConverter(_.toInt)
implicit val doubleConverter = toConverter(_.toDouble)
}
Then you can rewrite your method like this:
val map = Map("offset" -> "10", "price" -> "9.99")
def getQueryStringAs[T : Converter](key: String): Option[T] = {
val converter = implicitly[Converter[T]]
try {
Some(converter convert map(key))
} catch {
case ex: Exception => None
}
}
In use:
scala> getQueryStringAs[Int]("offset")
res1: Option[Int] = Some(10)
scala> getQueryStringAs[Double]("price")
res2: Option[Double] = Some(9.99)
Suppose that I have a string in scala and I want to try to parse a double out of it.
I know that, I can just call toDouble and then catch the java num format exception if this fails, but is there a cleaner way to do this? For example if there was a parseDouble function that returned Option[Double] this would qualify.
I don't want to put this in my own code if it already exists in the standard library and I am just looking for it in the wrong place.
Thanks for any help you can provide.
For Scala 2.13+ see Xavier's answer below. Apparently there's a toDoubleOption method now.
For older versions:
def parseDouble(s: String) = try { Some(s.toDouble) } catch { case _ => None }
Fancy version (edit: don't do this except for amusement value; I was a callow youth years ago when I used to write such monstrosities):
case class ParseOp[T](op: String => T)
implicit val popDouble = ParseOp[Double](_.toDouble)
implicit val popInt = ParseOp[Int](_.toInt)
// etc.
def parse[T: ParseOp](s: String) = try { Some(implicitly[ParseOp[T]].op(s)) }
catch {case _ => None}
scala> parse[Double]("1.23")
res13: Option[Double] = Some(1.23)
scala> parse[Int]("1.23")
res14: Option[Int] = None
scala> parse[Int]("1")
res15: Option[Int] = Some(1)
Scalaz provides an extension method parseDouble on Strings, which gives a value of type Validation[NumberFormatException, Double].
scala> "34.5".parseDouble
res34: scalaz.Validation[NumberFormatException,Double] = Success(34.5)
scala> "34.bad".parseDouble
res35: scalaz.Validation[NumberFormatException,Double] = Failure(java.lang.NumberFormatException: For input string: "34.bad")
You can convert it to Option if so required.
scala> "34.bad".parseDouble.toOption
res36: Option[Double] = None
scala> import scala.util.Try
import scala.util.Try
scala> def parseDouble(s: String): Option[Double] = Try { s.toDouble }.toOption
parseDouble: (s: String)Option[Double]
scala> parseDouble("3.14")
res0: Option[Double] = Some(3.14)
scala> parseDouble("hello")
res1: Option[Double] = None
Scala 2.13 introduced String::toDoubleOption:
"5.7".toDoubleOption // Option[Double] = Some(5.7)
"abc".toDoubleOption // Option[Double] = None
"abc".toDoubleOption.getOrElse(-1d) // Double = -1.0
You could try using util.control.Exception.catching which returns an Either type.
So using the following returns a Left wrapping a NumberFormatException or a Right wrapping a Double
import util.control.Exception._
catching(classOf[NumberFormatException]) either "12.W3".toDouble
Unfortunately, this isn't in the standard library. Here's what I use:
class SafeParsePrimitive(s: String) {
private def nfe[T](t: => T) = {
try { Some(t) }
catch { case nfe: NumberFormatException => None }
}
def booleanOption = s.toLowerCase match {
case "yes" | "true" => Some(true)
case "no" | "false" => Some(false)
case _ => None
}
def byteOption = nfe(s.toByte)
def doubleOption = nfe(s.toDouble)
def floatOption = nfe(s.toFloat)
def hexOption = nfe(java.lang.Integer.valueOf(s,16))
def hexLongOption = nfe(java.lang.Long.valueOf(s,16))
def intOption = nfe(s.toInt)
def longOption = nfe(s.toLong)
def shortOption = nfe(s.toShort)
}
implicit def string_parses_safely(s: String) = new SafeParsePrimitive(s)
There's nothing like this not only in Scala, but even in basic Java.
Here's a piece code that does it without exceptions, though:
def parseDouble(s: String)(implicit nf: NumberFormat) = {
val pp = new ParsePosition(0)
val d = nf.parse(s, pp)
if (pp.getErrorIndex == -1) Some(d.doubleValue) else None
}
Usage:
implicit val formatter = NumberFormat.getInstance(Locale.ENGLISH)
Console println parseDouble("184.33")
Console println parseDouble("hello, world")
I'd usually go with an "in place" Try:
def strTimesTen (s: String) = for (d <- Try(s.toDouble)) yield d * 10
strTimesTen("0.1") match {
Success(d) => println( s"It is $d" )
Failure(ex) => println( "I've asked for a number!" )
}
Note, that you can do further calculation in the for and any exception would project into a Failure(ex). AFAIK this is the idiomatic way of handling a sequence of unreliable operations.