18

I have a String array:

 String[] str = {"ab" , "fog", "dog", "car", "bed"};
 Arrays.sort(str);
 System.out.println(Arrays.toString(str));

If I use Arrays.sort, the output is:

 [ab, bed, car, dog, fog]

But I need to implement the following ordering:

FCBWHJLOAQUXMPVINTKGZERDYS

I think I need to implement Comparator and override compare method:

 Arrays.sort(str, new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            // TODO Auto-generated method stub
            return 0;
        }
    });

How should I go about solving this?

hamid
  • 2,033
  • 4
  • 22
  • 42
  • 2
    Firstly, you should consider using a character array if your items are always one character in length. Using strings forces more error checking. – Duncan Jones May 14 '13 at 10:59
  • @DuncanJones No they are not, this was just an example. I changed them. Thanks – hamid May 14 '13 at 11:02
  • @Sam: so each individual character in the string needs to be sorted by the order and then "normal" string rules apply? How is "AA" sorted relative to "A"? Or "FC" relative to "FB"? – Joachim Sauer May 14 '13 at 11:05
  • @JoachimSauer I don't know if I got your point correctly. But the order should be: "FC","FB","A","AA" – hamid May 14 '13 at 11:10

4 Answers4

43
final String ORDER= "FCBWHJLOAQUXMPVINTKGZERDYS";

Arrays.sort(str, new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
       return ORDER.indexOf(o1) -  ORDER.indexOf(o2) ;
    }
});

You can also add:

o1.toUpperCase()

If your array is case in-sensitive.


Apparently the OP wants to compare not only letters but strings of letters, so it's a bit more complicated:

    public int compare(String o1, String o2) {
       int pos1 = 0;
       int pos2 = 0;
       for (int i = 0; i < Math.min(o1.length(), o2.length()) && pos1 == pos2; i++) {
          pos1 = ORDER.indexOf(o1.charAt(i));
          pos2 = ORDER.indexOf(o2.charAt(i));
       }

       if (pos1 == pos2 && o1.length() != o2.length()) {
           return o1.length() - o2.length();
       }

       return pos1  - pos2  ;
    }
Egis
  • 5,081
  • 5
  • 39
  • 61
Majid Laissi
  • 19,188
  • 19
  • 68
  • 105
  • 3
    careful: upper/lower-case confusion abound. – Joachim Sauer May 14 '13 at 10:58
  • 1
    @MajidL Should that be case *in*-sensitive? – alex May 14 '13 at 11:06
  • @MajidL it doesn't seem to give correct result for: `{"AA","FB","A","FC"}`, it gives `[AA, FB, FC, A]`. – hamid May 14 '13 at 11:15
  • 4
    It's not correct. The answer provided would work only if you gave it 1 length string or strings that are a substring of ORDER (for example - JLO) – Avi May 14 '13 at 11:31
  • @MajidL that was only an example. So your solution wouldn't work for longer String? – hamid May 14 '13 at 11:41
  • @Avi I didn't see your answer.. and definitely didn't steal it :) (+1 for your answer) – Majid Laissi May 14 '13 at 11:44
  • @MajidL Thanks. it works perfectly, there was only one problem. For example for `{"AA", "A"}` and `{"A","AA"}`, it gives different result. But I managed to fixed it. Thanks again. – hamid May 14 '13 at 12:01
  • 1
    You could return `o1.length() - o2.length()` if `pos1 - pos2 == 0` to have the shortest word first if they match like `A` and `AA` for example. – maba May 14 '13 at 12:56
5

I would do something like this:

Put the letters in a HashTable (let's call it orderMap). Key is the letter, value is the index in ORDER.

And then:

Arrays.sort(str, new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
        int length = o1.length > o2.length ? o1.length: o2.length
        for(int i = 0; i < length; ++i) {
           int firstLetterIndex = orderMap.get(o1.charAt(i));
           int secondLetterIndex = orderMap.get(o2.charAt(i));

           if(firstLetterIndex == secondLetterIndex) continue;

           // First string has lower index letter (for example F) and the second has higher index letter (for example B) - that means that the first string comes before
           if(firstLetterIndex < secondLetterIndex) return 1;
           else return -1;
        }

        return 0;
    }
});

For making it case-insensitive just do toUpperCase() to both strings at the beginning.

Avi
  • 21,182
  • 26
  • 82
  • 121
0

Here you can find usefull link:

Using comparator to make custom sort

In your example instead comparing specific attributes of class you nedd to check possition of char in benchmarked String and base on this check if it's greather/equal/smaller.

Community
  • 1
  • 1
Jacek
  • 87
  • 7
0

Took my time to improve on the selected answer. This is more efficient

public static void customSort(final String order,String[] array){
String[] alphabets={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9"};
    String keyword=order;
    for(int g=0; g<alphabets.length; g++){
    String one=alphabets[g];
    if(!keyword.toUpperCase().contains(one)){keyword=keyword+one;}
    }

final String finalKeyword=keyword;
Arrays.sort(array, new Comparator<String>() {

    @Override
   public int compare(String o1, String o2) {
       int pos1 = 0;
       int pos2 = 0;
       for (int i = 0; i < Math.min(o1.length(), o2.length()) && pos1 == pos2; i++) {
          pos1 = finalKeyword.toUpperCase().indexOf(o1.toUpperCase().charAt(i));
          pos2 = finalKeyword.toUpperCase().indexOf(o2.toUpperCase().charAt(i));
       }

       if (pos1 == pos2 && o1.length() != o2.length()) {
           return o1.length() - o2.length();
       }

       return pos1  - pos2  ;
    }
});
//Arrays.sort(array, Collections.reverseOrder());
}