Reputation: 127
(I use Java)
I want to sort a sublist of objects by a property using a Collator so that is sorted by alphabetical order but ignoring accents. Problem is I have tried different things and none work.
This sorts the sublists but doesn't ignore accents:
newList.subList(0, 5).sort(Comparator.comparing(element -> element.getValue()));
This is the collator I want to use:
Collator spCollator = Collator.getInstance(new Locale("es", "ES"));
I expect the output to be a sublist sorted by alphabetical order by the property which you can access with .getValue() ignoring the accents.
Upvotes: 0
Views: 6293
Reputation: 115952
Actually, according to the documentation, it's better not to use the Collator for sorting directly, but to use the CollationKey instead:
For comparing Strings exactly once, the compare method provides the best performance. When sorting a list of Strings however, it is generally necessary to compare each String multiple times. In this case, CollationKeys provide better performance. The CollationKey class converts a String to a series of bits that can be compared bitwise against other CollationKeys. A CollationKey is created by a Collator object for a given String.
https://docs.oracle.com/javase/7/docs/api/java/text/Collator.html
https://kodejava.org/how-do-i-sort-strings-data-using-collationkey-class/
So, example from this article:
public class CollationKeyExample {
public static void main(String[] args) {
String[] countries = {
"German",
"United Kingdom",
"United States",
"French",
"Japan",
"Myanmar",
"India"
};
System.out.println("original:");
System.out.println(Arrays.toString(countries));
// Gets Collator object of default locale
Collator collator = Collator.getInstance();
// Creates and initializes CollationKey array
CollationKey[] keys = new CollationKey[countries.length];
for (int i = 0; i < countries.length; i++) {
// Generate CollationKey by calling
// Collator.getCollationKey() method then assign into
// keys which is an array of CollationKey.
// The CollationKey for the given String based on the
// Collator's collation rules.
keys[i] = collator.getCollationKey(countries[i]);
}
// Sort the keys array
Arrays.sort(keys);
// Print out the sorted array
System.out.println("sorted result: ");
StringBuilder sb = new StringBuilder();
for (CollationKey key : keys) {
sb.append(key.getSourceString()).append(",");
}
System.out.println(sb);
}
}
And, if you want to have a Kotlin solution (works in Java too, of course), you can have a more generic solution by storing the CollationKey for each item (using a HashMap, for example):
fun ArrayList<String>.sortUsingCollator(collator: Collator = Collator.getInstance()) {
if (size <= 1)
return
val hashMap = HashMap<String, CollationKey>(size)
sortWith { o1, o2 ->
val key1 = hashMap.getOrPut(o1) {
collator.getCollationKey(o1)
}
val key2 = hashMap.getOrPut(o2) {
collator.getCollationKey(o2)
}
key1.compareTo(key2)
}
}
fun <T> ArrayList<T>.sortUsingCollator(collator: Collator = Collator.getInstance(), getStringValue: (input: T) -> String) {
if (size <= 1)
return
val hashMap = HashMap<String, CollationKey>(size)
sortWith { o1, o2 ->
val o1Str = getStringValue(o1)
val o2Str = getStringValue(o2)
val key1 = hashMap.getOrPut(o1Str) {
collator.getCollationKey(o1Str)
}
val key2 = hashMap.getOrPut(o2Str) {
collator.getCollationKey(o2Str)
}
key1.compareTo(key2)
}
}
Usage:
val testList = arrayListOf(
"asd", "ézxc", "âxc", "bbb", "etas", "àzxc", "ASD", "aSd"
)
//useful in case the list is of strings:
testList.sortUsingCollator()
//this is useful in case the list isn't of string:
testList.sortUsingCollator { it }
Upvotes: 0
Reputation: 4309
Collator is also a Comparator. If the elements are String:
List<String> list = Arrays.asList("abc", "xyz", "bde");
Collator spCollator = Collator.getInstance(new Locale("es", "ES"));
list.sort(spCollator);
If the elements are custom Object:
List<Element> list = Arrays.asList(new Element("abc"), new Element("xyz"), new Element("bde"), new Element("rew"), new Element("aER"),
new Element("Tre"), new Element("ade"));
list.subList(0, 4).sort(new MyElementComparator());
System.out.println(list);
private static class MyElementComparator implements Comparator<Element>{
Collator spCollator = Collator.getInstance(new Locale("es", "ES"));
public int compare (Element e1, Element e2){
return spCollator.compare(e1.getValue(), e2.getValue());
}
}
Or the lambda way:
List<Element> list = Arrays.asList(new Element("abc"), new Element("xyz"), new Element("bde"), new Element("rew"), new Element("aER"),
new Element("Tre"), new Element("ade"));
Collator spCollator = Collator.getInstance(new Locale("es", "ES"));
list.subList(0, 4).sort((e1, e2)-> spCollator.compare(e1.getValue(), e2.getValue()));
System.out.println(list);
Upvotes: 6
Reputation: 15008
Instead of using Comparator.comparing
, you create a lambda to first extract the value and then use the collator to compare.
Collator spCollator = Collator.getInstance(new Locale("es", "ES"));
newList.subList(0, 5).sort((e1, e2) -> spCollator.compare(e1.getValue(), e2.getValue()));
Upvotes: 1