User1
User1

Reputation: 127

How can I sort by property using a collator ? (Java)

(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

Answers (3)

android developer
android developer

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

fiveobjects
fiveobjects

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

daniu
daniu

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

Related Questions