Regex to retrieve last digits from a string and sort them

Viewed 129

I have a list of string which I want to sort them using the last digits present in the string, I have tried this using below code, but for some reasons, it's picking digit present before the last digit as well, for instance in "abc\xyz 2 5" string it's picking 25 instead of just 5 because of which it is sorting it incorrectly. May I know what's incorrect in my regex?

Note: My last two digits will always be timestamp like 1571807700009 1571807700009.

Here's what I have tried so far.

public static void second() {   
List<String> strings = Arrays.asList("abc\\xyz 2 5", "abc\\\\xyz 1 8", "abc\\\\xyz 1 9", "abc\\\\xyz 1 7", "abc\\\\xyz 1 3");       
Collections.sort(strings, new Comparator<String>() {
        public int compare(String o1, String o2) {
            return (int) (extractInt(o1) - extractInt(o2));
        }

        Long extractInt(String s) {
            String num = s.replaceAll("\\D", "");
            return Long.parseLong(num);
        }
    });
    System.out.println(strings);

}

Output

[abc\\xyz 1 3, abc\\xyz 1 7, abc\\xyz 1 8, abc\\xyz 1 9, abc\xyz 2 5]

Expected Output

[abc\\xyz 1 3, abc\\xyz 2 5, abc\\xyz 1 7, abc\\xyz 1 8, abc\xyz 1 9]
4 Answers

Change your extractInt method to this to remove everything except last number from input:

Long extractInt(String s) {
    String num = s.replaceFirst("^.+\\b(\\d+)$", "$1");
    return Long.parseLong(num);
}

This regex is matching a greedy match at start .+ to make sure to match longest string before matching \d+ in the end after matching word boundary using \b.

This will give following output:

[abc\\xyz 1 3, abc\xyz 2 5, abc\\xyz 1 7, abc\\xyz 1 8, abc\\xyz 1 9]

Using a stream, sort only on the last integer by replacing the previous portion of the string with an empty string. You can also take advantage of the API Comparator interface by passing that value to the comparing method.

   List<String> strings = Arrays.asList("abc\\xyz 2 5", "abc\\\\xyz 1 8",
      "abc\\\\xyz 1 9", "abc\\\\xyz 1 7", "abc\\\\xyz 1 3");

   strings = strings.stream()
       .sorted(Comparator.comparing(s -> Long.valueOf(s.replaceAll(".*\\s+", ""))))
       .collect(Collectors.toList());

   System.out.println(strings);

If your goal is to sort your strings by comparing only the last digit, you even don't need to parse that digit to int or long. Assuming your strings have always a digit at the end:

Function<String,String> lastDigit = s -> s.substring(s.length()-1);
List<String> strings = Arrays.asList("abc\\xyz 2 5", "abc\\\\xyz 1 8", "abc\\\\xyz 1 9", "abc\\\\xyz 1 7", "abc\\\\xyz 1 3"); 

System.out.println("Before sorting: " + strings);

strings.sort(Comparator.comparing(lastDigit));

System.out.println("After sorting: " + strings);

EDIT

You don't seem to compare only the last digit as assumed at the beginning, but the last number after the last space character. If this is the case use the similar approach below

Function<String,Long> lastNum = s -> Long.valueOf(s.substring(s.lastIndexOf(" ")+1));
List<String> strings = Arrays.asList("abc\\xyz 2 5", "abc\\\\xyz 1 8", "abc\\\\xyz 1 9", "abc\\\\xyz 1 7", "abc\\\\xyz 1 3"); 

System.out.println("Before sorting: " + strings);

strings.sort(Comparator.comparing(lastNum));

System.out.println("After sorting: " + strings);

To compare the last number in each string you can just substring from the last space and then parse to a Long.

I.e.

strings = strings.stream().sorted(Comparator.comparing(
        s -> parseLong(s.substring(s.lastIndexOf(' ') + 1))
    )).collect(Collectors.toList());
Related