Reputation: 515
Basically i have an arraylist of objects like so:
[{name: 'A'}, {name: 'B'}, {name:'?'}]
I want to sort these so that the question mark is at the end like above.
But using the below code.
Collections.sort(myList);
This always results in the object with the question mark first, I think this is due to ASCII ordering? I think the correct way forward is to use a comparator function but i'm not sure how that would take shape with letters and special characters?
How would I implement this?
Upvotes: 2
Views: 1804
Reputation: 21918
An alternative approach - one which sorts any punctuation to the end - can be achieved using a rules-based collator.
An example:
List<String> words = Arrays.asList(
"?dog", "rat", "456", "123", "dog", "pig", "?cat", "!rat", "cat"
);
String englishRules = ("< a,A < b,B < c,C < d,D < e,E < f,F "
+ "< g,G < h,H < i,I < j,J < k,K < l,L "
+ "< m,M < n,N < o,O < p,P < q,Q < r,R "
+ "< s,S < t,T < u,U < v,V < w,W < x,X "
+ "< y,Y < z,Z < 0,1,2,3,4,5,6,7,8,9");
RuleBasedCollator rbc = new RuleBasedCollator(englishRules);
rbc.setStrength(Collator.PRIMARY);
Collections.sort(words, rbc);
words.forEach((word) -> {
out.print(word + " ");
});
This outputs:
cat dog pig rat 123 456 !rat ?cat ?dog
Points to note:
1) This specific example is limited to an English collation.
2) The general technique works because all non-mentioned characters are sorted to the end. So, not only are punctuation symbols sorted after English letters and digits - but so is every other character/symbol (e.g. those used by other scripts).
3) If you want a non-Unicode ordering of punctuation symbols, they need to be surrounded by single quotes in your rule string:
"... < 0,1,2,3,4,5,6,7,8,9 < '?' < '!'"
Upvotes: 2
Reputation: 11621
You can use some useful methods from java.util.Comparator
to make your life easier and your code less error-prone than having to think about if-else or ternary operators:
class MyObj {
private String name;
MyObj(String name) {
this.name = name;
}
String getName() {
return name;
}
@Override
public String toString() {
return "{name: '" + name + "'}";
}
}
public class Demo {
public static void main(String[] args) {
List<MyObj> lst = Arrays.asList(new MyObj("B"), new MyObj("?"), new MyObj("A"));
Comparator<String> questionMarksLast = Comparator
.<String, Boolean>comparing("?"::equals)
.thenComparing(Comparator.naturalOrder());
lst.sort(Comparator.comparing(MyObj::getName, questionMarksLast));
System.out.println(lst); // prints [{name: 'A'}, {name: 'B'}, {name: '?'}]
}
}
Upvotes: 1
Reputation: 521429
In Java 8, you may use a two-tiered custom comparator:
// given
List<YourObject> list;
list.sort((o1, o2) -> "?".equals(o1.getName()) ? 1 :
("?".equals(o2.getName()) ? -1 : o1.getName().compareTo(o2.getName())));
The sorting logic here is that if one or the other names be ?
, then we always sort that ?
last. If both names be ?
, or if neither be ?
, then we sort using the default lexicographical string sorting.
Upvotes: 1