Since I advocated to reopen this, here's a Comparator<String>
that will do what you want:
package testJ;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class WeirdOrderComparator implements Comparator<String> {
private static List<String> COMPASS_ORDER = Arrays.asList("E:S:W:N"
.split(":"));
private static List<String> D_ORDER = Arrays.asList("W:G:R".split(":"));
private static List<String> SUFFIX_ORDER = Arrays.asList("m:p:s::d"
.split(":"));
private int indexFor(String target, List<String> order) {
int r = order.indexOf(target);
if (r < 0) {
return order.size();
} else {
return r;
}
}
private int getSuffixNumber(String s) {
switch (s.length()) {
case 1:
return indexFor("", SUFFIX_ORDER);
case 2:
return indexFor(s.substring(1), SUFFIX_ORDER);
default:
return 99;
}
}
private int getWithinGroupNumber(int suffixGroup, String s) {
switch (suffixGroup) {
case 0:
case 1:
case 2:
return Integer.valueOf(s.substring(0, s.length() - 1));
case 3:
return indexFor(s, COMPASS_ORDER);
case 4:
return indexFor(s.substring(0, 1), D_ORDER);
default:
return 99;
}
}
public int compare(String o1, String o2) {
int sfx1 = getSuffixNumber(o1);
int sfx2 = getSuffixNumber(o2);
if (sfx1 != sfx2) {
return sfx1 - sfx2;
}
int grp1 = getWithinGroupNumber(sfx1, o1);
int grp2 = getWithinGroupNumber(sfx2, o2);
if (grp1 != grp2) {
return grp1 - grp2;
}
return o1.compareTo(o2);
}
}
It can be used as:
package testJ;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class WeirdOrderTest {
public static void main(String args[]) {
List<String> testList = Arrays.asList("N 2p 9s 9s 1p 9m N 4s 1m 5p 7m 2s 5s 5m".split(" "));
System.out.println("Original order: " + testList);
ArrayList<String> sorted = new ArrayList<String>();
sorted.addAll(testList);
Collections.sort(sorted, new WeirdOrderComparator());
System.out.println("Sorted: " + sorted);
}
}
This produces:
Original order: [N, 2p, 9s, 9s, 1p, 9m, N, 4s, 1m, 5p, 7m, 2s, 5s, 5m]
Sorted: [1m, 5m, 7m, 9m, 1p, 2p, 5p, 2s, 4s, 5s, 9s, 9s, N, N]
The general strategy here is:
find a rule that might distinguish between two strings. In this case, I first identify the general "group" of the string: things that end in "m", in "p", in "s", that are single letters, or that end in "d".
If that rule distinguishes the two inputs, return a number that is negative if o1
should come first, and positive if o1
should come later. To remember this convention, think "it's like o1 - o2
if o1
and o2
are small integers" (ignoring overflow).
If that rule doesn't distinguish o1
and o2
, find another rule, etc.
Finally, if you run out of rules, return 0 or delegate to some other comparison.
Remember in this that your Comparator
should reasonably handle strings that you don't expect - it can throw an exception (as this one will if you give it "Xs"), but if it doesn't throw an exception then the result should be consistent. That is, if compare(x, y) < 0
and compare(y, z) < 0
then if compare(x, z)
doesn't throw an exception, compare(x, z)
must be negative, regardless of what x
, y
, and z
are. One way to ensure that is to structure your comparator the way I did here, where I find a series of "order numbers" for each string and then delegate to String.compareTo
if the order numbers are no help.