Search code examples
javacomparecomparatorstring-comparison

Java Custom Size String Comparator


I have the following Strings:

50ml,100g,3.5ml,10g,0.4g,320ml and I need to order them as follows:

0.4g,10g,100g,3.5ml,50ml,320ml

The criteria is order the type of size measure alphabetically (g comes before m) and then the actual size ASCENDANT (0.4 10g 100g)

How can I achieve this using a Comparator?

protected int compareInstances(String instance1, String instance2) {
    return 0;
}

Solution

  • Probably the easiest (however not the prettiest) approach to take here is to simply split the values into prefix (actual value) and suffix (measurement), to then compare these seperately.

    So I implemented a Comparator<String>, which splits the given values into prefix and suffix, then firstly compares the suffix alphabetically. (Disclaimer: Uppercase letters will be considered as "smaller" than the lowercase equivalent) If they are equal, the prefix values are compared.

    Here is a small example with the described logic:

    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Test {
    
        public static void main(String[] args) {
            String input = "50ml,100g,3.5ml,10g,0.4g,320ml,32.3a,3.6ml";
            String[] splitInput = input.split(",");
            System.out.println("Before:\t" + Arrays.toString(splitInput));
            Arrays.sort(splitInput, new MyComparator());
            System.out.println("After:\t" + Arrays.toString(splitInput));
        }
    
        static class MyComparator implements Comparator<String> {
    
            @Override
            public int compare(String o1, String o2) {
                // extract the suffix by removing the digits and dots
                String suffix1 = o1.replaceAll("[\\d\\.]", "");
                String suffix2 = o2.replaceAll("[\\d\\.]", "");
                if (suffix1.compareTo(suffix2) != 0) {
                    return suffix1.compareTo(suffix2); // String#compareTo
                }
                // extract the prefix by removing the characters
                double value1 = Double.parseDouble(o1.replaceAll("[A-Za-z]", ""));
                double value2 = Double.parseDouble(o2.replaceAll("[A-Za-z]", ""));
                // compare the double values
                return (int) Double.compare(value1, value2);
            }
        }
    
    }
    

    Output:

    Before: [50ml, 100g, 3.5ml, 10g, 0.4g, 320ml, 32.3a, 3.6ml]
    After:  [32.3a, 0.4g, 10g, 100g, 3.5ml, 3.6ml, 50ml, 320ml]