# Extracting numeric values from a string containing key:value pairs

I am writing function numericValues(text: String): List[Int] to extract patterns """([a-z]+)\s*:\s*(\d+)""" and return the list of the numeric values :

numericValues("a123 : 0 abc:123 123:abc xyz:1") // List(123, 1)


I would write numericValues like this:

def numericValues(text: String): List[Int] = {
val regex = """([a-z]+)\s*:\s*(\d+)""".r
regex.findAllIn(text).toList.flatMap {s =>
PartialFunction.condOpt(s) { case regex(_, num) => num.toInt }
}
}


I guess the condOpt invocation is redundant and I wonder how to simplify this implementation. Also, I'd appreciate comments on improvements to style and best practice.

You usually want a zero-width word boundary \b on either end of the regex, to avoid matching things like 1a:1a.

There's no need to capture [a-z]+ since you are throwing it away.

You can use lookbehind (?<=…) assertions to require a match to be preceded by whatever, without including the whatever in the match result. This means no need for capturing parenthesis, only the integer is included in the match, and the final map is simply _.toInt. Variable-width lookbehind was introduced in Java 9; older versions have only fixed-width lookbehind.

Finally, removing the variable makes braces unnecessary.

def numericValues(text: String): List[Int] = """(?<=\b[a-z]+\s*:\s*)\d+\b""".r.findAllIn(text).toList.map(_.toInt)


With only fixed-width lookbehind, you could postprocess the matches to remove non-numerics:

….map(_.replaceAll("[^0-9]", "").toInt)


Or, more idiomatically and less hacky, just capture the digits and extract the capture groups:

"""\b[a-z]+\s*:\s*(\d+)\b""".r.findAllIn(text).matchData.toList.map(_.group(1).toInt)

• Thanks a lot. What if I have only fixed-width lookbehind ? – Michael Mar 20 at 13:00
• then you can't use variable-width lookbehind :) See edit. – Oh My Goodness Mar 20 at 13:56