Reputation: 8584
I'm trying to figure out what the best way to pattern match a string representation of an int in Scala. What I really want to be able to do is something like this:
"1234" match {
// Some cases
case "five" => 5
case Int(i) => i // Fails
case _ => throw new RuntimeException()
}
One approach is this, using a regular expression. A potential problem with this is that it won't detect if the integer is too large to fit in an int.
val ISINT = "^([+-]?\\d+)$".r
"1234" match {
case "five" => 5
case ISINT(t) => t.toInt
case _ => throw new RuntimeException()
}
Another approach uses a toInt
function that returns an Option
(borrowed from this blog post). This is nice because it makes the standard library figure out whether the string contains an integer. The problem is that it forces me to nest my logic where I think it should be flat.
def toInt(s: String): Option[Int] = {
try {
Some(s.toInt)
} catch {
case e: Exception => None
}
}
"1234" match {
case "five" => 5
case t => toInt(t) match {
case Some(i) => i
case None => throw new RuntimeException()
}
}
Upvotes: 6
Views: 4696
Reputation: 5700
Just another trick with regex
"1234" match {
case number if number.matches("\\d+") => println(s"yes its a numeric value $number")
case _ => println("not a numeric value")
}
Upvotes: 1
Reputation: 30320
I really like @Jasper-M's approach for its simplicity, but I do my best to avoid generating exceptions as a means of control flow--not to mention the significant performance cost. The interesting thing is that @Jasper-M's approach is so concise and elegant it hides these details.
So if you want to try a non-exception based approach for fun:
val matcher = "1234" match {
case "five" => Some(BigInt(5L))
case regex(t) => Some(BigInt(t))
case _ => None
}
val result: Option[Int] = matcher.filter(_.isValidInt).map(_.toInt)
In this case, you get an Option[Int]]
, which is a Some
for a good Int
parsed from the String
and None
otherwise.
I would go with @Jasper-M's approach first and watch performance. If you notice anything, then consider non-exception-based approaches.
Upvotes: 2
Reputation: 22625
Maybe you could use guard in match?
"12312312313123" match {
case s: String if s.isInt => "int"
case _ => "no int"
}
implicit class IsInt(i: String){
def isInt: Boolean = Try(i.toInt).isSuccess
}
Upvotes: 0
Reputation: 15086
You can define a custom extractor like this
object Int {
def unapply(s: String): Option[Int] = util.Try(s.toInt).toOption
}
And use it like you wanted to
"1234" match {
// Some cases
case "five" => 5
case Int(i) => i // works :)
case _ => throw new RuntimeException()
}
Upvotes: 12