2

i'm trying to sort a list of strings based on a custom sort order by defining some prefixes. the result should be ordered based on the order of the prefixes and the none matching entries should be at the end of the list. when multiple strings are starting with the same prefix or there are multiple entries in the rest the should be sorted alphabetically.

already tried a custom comparator based on a soltion from another thread How to sort a java list against a custom order. But i'm now stuck at the last part of my requirement to bring the remaining entries which do not have a prefix at the end of the list.

final Comparator<String> comparator = new PrefixComparator(Arrays.asList("java.", "javax.", "org."));
final ArrayList<String> imports = Lists.newArrayList("java.util.Arrays", "javax.annotation.Generated", "org.testng.annotations.Test", "org.testng.annotations.Optional", "de.bigmichi1.test.ClassB");

Collections.shuffle(imports);

imports.sort(comparator);

Assertions.assertThat(imports).containsExactly("java.util.Arrays", "javax.annotation.Generated", "org.testng.annotations.Optional","org.testng.annotations.Test", "de.bigmichi1.test.ClassB");
public class PrefixComparator implements Comparator<String>, Serializable {
    private static final long serialVersionUID = -5748758614646881440L;
    private final List<String> customOrder;

    public PrefixComparator(@Nonnull final List<String> customOrder) {
        this.customOrder = customOrder;
    }

    @Override
    public int compare(final String o1,
            final String o2) {
        int order1 = -1;
        int order2 = -1;
        String remainder1 = "";
        String remainder2 = "";
        for (final String prefix : customOrder) {
            if (o1.startsWith(prefix)) {
                order1 = customOrder.indexOf(prefix);
                remainder1 = o1.substring(prefix.length());
            }
            if (o2.startsWith(prefix)) {
                order2 = customOrder.indexOf(prefix);
                remainder2 = o2.substring(prefix.length());
            }
        }
        if (order1 == order2) {
            return remainder1.compareTo(remainder2);
        } else {
            return order1 - order2;
        }
    }
}
BigMichi1
  • 307
  • 1
  • 11
  • 1
    If a string doesn't start with a prefix, then the remainder shouldn't be the empty string. It should be the string itself. And if a string doesn't start with a prefix, since you want it at the end of the sorted list, its order shouldn't be -1 (which is lower than the other orders), but, `customOrder.size()`, which is bigger than the other orders. – JB Nizet Sep 08 '19 at 08:31

1 Answers1

2

Here is how I would do it:

import java.util.*;

public class Test
{
    public static void main(String[] args) {
        List<String> prefix = Arrays.asList("java.", "javax.", "org.");
        List<String> imports = Arrays.asList("org.testng.annotations.Test", "java.util.Arrays", "javax.annotation.Generated", "org.testng.annotations.Optional", "de.bigmichi1.test.ClassB");
        Collections.sort(imports, new Comparator<String>(){
            int prefix1Index = Integer.MAX_VALUE, prefix2Index = Integer.MAX_VALUE;
            @Override
            public int compare(String o1, String o2) {
                prefix1Index = Integer.MAX_VALUE;
                prefix2Index = Integer.MAX_VALUE;
                prefix.forEach((pre) -> {
                    if(o1.startsWith(pre)){
                        prefix1Index = prefix.indexOf(pre);
                    }
                    if(o2.startsWith(pre)){
                        prefix2Index = prefix.indexOf(pre);
                    }
                });

                if(prefix1Index == prefix2Index) return o1.compareTo(o2);
                else return prefix1Index - prefix2Index;
            }
        });

        imports.forEach((imported) -> {
            System.out.println(imported);
        });

    }
}

Here is the output:

java.util.Arrays
javax.annotation.Generated
org.testng.annotations.Optional
org.testng.annotations.Test
de.bigmichi1.test.ClassB
Ahmed AL-Yousif
  • 430
  • 2
  • 9