0

I want to sort an ArrayList of strings of length 1 or 2 in a specific order.

the order is

1m, 2m, 3m ... 9,, 1p...9p, 1s...9s, E, S, W, N,Wd, Gd, Rd

if you get the idea?

For example, the ArrayList is;

N 2p 9s 9s 1p 9m N 4s 1m 5p 7m 2s 5s 5m  

I am trying to write a method in the class where this ArrayList is a field that will put it in the desired order.

Questions similar point to comparators, but I've found every explanation very confusing, but doing something different than I want anyway.

Id be perfectly fine with just writing the order out completely.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html – ares Jul 04 '15 at 14:36
  • 3
    *if you get the idea?* No, frankly, I don't. If you can't clearly define the rule telling if a string is before or after another one, then you can't implement it. This rule is known as a Comparator. – JB Nizet Jul 04 '15 at 14:36
  • What have you found confusing about the comparator approach? That's exactly what you need to do. Ask for clarification on the original question – please don't ask the same question again. – Matt Ball Jul 04 '15 at 14:36
  • Do you want to sort by the numeric part first then the alphabetic part case sensitive? – A4L Jul 04 '15 at 14:36
  • 3
    I agree that this question may have some issues, but it emphatically **is not** a duplicate of the linked question. His problem is that he doesn't understand how to write a compareTo method for his situation. Closing with a link to a "here's how you build a comparator for fields that already have a compareTo" answer is *really unhelpful*. – Daniel Martin Jul 04 '15 at 14:42
  • Also voting to reopen; Daniel Martin is correct above that this isn't a duplicate, and they just need help understanding comparators. – Dean J Jul 04 '15 at 14:55
  • Ok, leave it closed. Im not even sure if I have got the idea or not yet. I asked the question very poorly. I now assigned the Objects in the array an int to define priority by what the name.endsWith(). It half works, but grouping the endswith in the right order, but within the same endswith (m/s/p), the numbers are still out of order. I should be able to find out how to do 2 levels of sorting now –  Jul 04 '15 at 14:58

1 Answers1

1

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:

  1. 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".

  2. 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).

  3. If that rule doesn't distinguish o1 and o2, find another rule, etc.

  4. 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.

Daniel Martin
  • 23,083
  • 6
  • 50
  • 70