Reputation: 6420
I have a List of colors like this:
Pink, Blue, Red, blue, Grey, green, purple, black ...etc
List<String> listOfColors = Arrays.asList("Pink", "Blue", "Red", "blue", "Grey", "green", "purple", "black");
There are some intermediate operation like filtering some fruit colors, now I am left with filtered results where I want them to be sorted in order:
Blue, black, blue, Grey, green, Pink, purple, Red
I have tried :
List<String> collect = listOfColors.stream().sorted(String::compareToIgnoreCase)
.collect(Collectors.toList());
It does not work as expected.
The output is the following:
black, Blue, blue, green, Grey, Pink, purple, Red
I want the following:
Blue, black, blue, Grey, green, Pink, purple, Red
Upvotes: 11
Views: 1410
Reputation: 225352
You need a method that first does a case insensitive comparison on each letter, then if there's a match perform a case sensitive comparison on each letter:
public static int compare(String s1, String s2)
{
int len, i;
if (s1.length()<s2.length()) {
len = s1.length();
} else {
len = s2.length();
}
for (i=0;i<len;i++) {
if (Character.toUpperCase(s1.charAt(i)) < Character.toUpperCase(s2.charAt(i))) {
return -1;
} else if (Character.toUpperCase(s1.charAt(i)) > Character.toUpperCase(s2.charAt(i))) {
return 1;
} else if (s1.charAt(i) < s2.charAt(i)) {
return -1;
} else if (s1.charAt(i) > s2.charAt(i)) {
return 1;
}
}
if (s1.length() < s2.length()) {
return -1;
} else if (s1.length() > s2.length()) {
return 1;
} else {
return 0;
}
}
You can then pass this method to Stream.sorted
.
Upvotes: 0
Reputation: 6420
You can use RuleBasedCollator to define your own Rules.
Example of custom rule:
String rules = "< c,C < b,B";
The above rule is decoded as that both uppercase and lowercase
C
's are to appear before both uppercase and lowercaseB
's when comparing strings.
String customRules = "<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<X<x<Y<y<Z<z";
RuleBasedCollator myRuleBasedCollator = new RuleBasedCollator(customRules);
Collections.sort(listOfColors,myRuleBasedCollator);
System.out.println(listOfColors);
Output:
[Blue, black, blue, Grey, green, Pink, purple, Red]
Edit: Instead of writing the customRules
by hand, you can use the below to code to generate it.
String a = IntStream.range('a', 'z' + 1).mapToObj(c -> Character.toString((char) c))
.flatMap(ch -> Stream
.of("<", ch.toUpperCase(), "<", ch)).collect(Collectors.joining(""));
Upvotes: 9
Reputation: 952
My solution is to use sorting in two steps by using the Comparator.thenComparing()
method.
First, compare the Strings only by the first character ignoring case. So the groups with the same first character (no matter what case) remain unsorted so far. Then in the second step apply the normal alphabetical sorting to sort those unsorted subgroups.
List<String> listOfColors = Arrays.asList("Pink", "Blue", "Red", "blue", "Grey", "green", "purple", "black");
Comparator<String> comparator = Comparator.comparing(s ->
Character.toLowerCase(s.charAt(0)));
listOfColors.sort(comparator.thenComparing(Comparator.naturalOrder()));
System.out.println(listOfColors);
Maybe it can be still optimized, but it gives the desired result:
[Blue, black, blue, Grey, green, Pink, purple, Red]
Upvotes: 8