Brian McCutchon
Brian McCutchon

Reputation: 8584

How to pattern match int strings?

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

Answers (4)

Balaji Reddy
Balaji Reddy

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

Vidya
Vidya

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

Krzysztof Atłasik
Krzysztof Atłasik

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

Jasper-M
Jasper-M

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

Related Questions