Regex one liner to extract pattern matching groups

Viewed 500

Using Java 11 I wonder how I can use the Stream API to extract all the group matches to a list of strings in a one-liner.

Given this regexp and string:

String regexp = "(\\d\\d\\d)-(\\d)-(\\d\\d)";
String str = "123-8-90";

I know how to obtain the result in multiple lines:

Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(str);

List<String> matches = new ArrayList<>();
if (matcher.find()) {
  matches.add(matcher.group(1));
  matches.add(matcher.group(2));
  matches.add(matcher.group(3));
}

System.out.println(matches);

This prints the expected list of 3 distinct numeric strings: [123, 8, 90]

My attempt to achieve the same in one liner:

List<String> matches = Pattern.compile(regexp)
    .matcher(str)
    .results()
    .map(MatchResult::group)
    .collect(Collectors.toList());

System.out.println(matches);

This prints the unexpected: [123-8-90]

How can I use the MatchResult::group(int) method in the stream?

2 Answers

You were pretty close. You need to flat map the MatchResult#group:

List<String> matches = Pattern.compile(regexp)
    .matcher(str)
    .results()
    .flatMap(mr -> IntStream.rangeClosed(1, mr.groupCount())
        .mapToObj(mr::group))
    .collect(Collectors.toList());

System.out.println(matches);

Your unnecessarily complex regex has made your solution error-prone and complicated. A couple of the easier ways to do it can be:

  1. Use the regex, \d+ and map the Stream to MatchResult::group.
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> matches = 
                Pattern.compile("\\d+")
                        .matcher("123-8-90")
                        .results()
                        .map(MatchResult::group)
                        .collect(Collectors.toList());

            System.out.println(matches);// [123, 8, 90]
    }
}

ONLINE DEMO

  1. Split on -
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> matches = Arrays.asList("123-8-90".split("-"));
        System.out.println(matches);// [123, 8, 90]
    }
}

ONLINE DEMO

In addition to solutions using regex, the characters of the string can be traversed and the numbers can be collected into a List. As with regex solutions, there can be many ways to do this as well.

Related