1

I want to sort the Turkish name of countries in Dart, however I couldn't find a method to customize the alphabetical order during sorting operation like collations in Java.

For example when I sort with a standard string compareTo method I get:

var countries = ["Belgrad", "Belçika", "Irak", "İran", "Sudan", "Şili", "Çek Cumhuriyeti", "Cezayir", "Ukrayna", "Ürdün"];

countries.sort((firstString, secondString) => firstString.compareTo(secondString));
print(countries); 

It prints in a wrong order based on English alphabetical order:

[Belgrad, Belçika, Cezayir, Irak, Sudan, Ukrayna, Çek Cumhuriyeti, Ürdün, İran, Şili]

Because in Turkish alphabet the order of letters is "a", "b", "c", "ç", "d", "e", "f", "g", "ğ", "h", "ı", "i", "j", "k", "l", "m", "n", "o", "ö", "p", "r", "s", "ş", "t", "u", "ü", "v", "y", "z", the correct order should be like: (note that Belçika must come before Belgrad because ç < g in Turkish)

[Belçika, Belgrad, Cezayir, Çek Cumhuriyeti, Irak, İran, Sudan, Şili, Ukrayna, Ürdün]

alierdogan7
  • 600
  • 7
  • 14

2 Answers2

3

You can define what sorting method you want. If I take your example, you can do something like:

void main() {
  const List<String> turkishOrder = ["a", "b", "c", "ç", "d", "e", "f", "g", "ğ", "h", "ı", "i", "i̇", "j", "k", "l", "m", "n", "o", "ö", "p", "r", "s", "ş", "t", "u", "ü", "v", "y", "z"];
  List<String> countries = ["Irak", "İran", "Sudan", "Şili", "Çek Cumhuriyeti", "Cezayir", "Ukrayna", "Ürdün"];

  countries.sort((String a, String b) => turkishOrder.indexOf(a[0].toLowerCase()).compareTo(turkishOrder.indexOf(b[0].toLowerCase())));
  
  print(countries);
}

What I did here:

  • I define the Turkish order in a List,
  • then to compare 2 countries, I retrieve the first letter (a[0]), to lower case since my turkishOrder array contains only lowercase letters
  • I retrieve the index of this lowercase first letter, to compare to the second country.

Notes:

  • I did not check if the country in countries List is not empty, a[0] or b[0] would throw an exception in this case, don't forget it.

  • I also had to add "i̇" in the turkishOrder List, in order to have İran at the right place.

Edit: the solution above only compares the first letter. Here is an updated version of the comparison function, to compare all letters:

  countries.sort((String a, String b) {
    int index = 0;
    while (index < a.length && index < b.length) {
      final int comparison =
          turkishOrder.indexOf(a[index].toLowerCase()).compareTo(turkishOrder.indexOf(b[index].toLowerCase()));
      if (comparison != 0) {
        // -1 or +1 means that the letters are different, thus an order is found
        return comparison;
      } // 0 means that the letters are equal, go to next letter
      index++;
    }
    return 0;
  });
lucie
  • 895
  • 5
  • 19
  • Thanks for your answer! Although it gives a correct output for my example of country names, this solution only looks at the first letters of the strings. I think a full comparison for strings is necessary to handle all possible cases. I added two additional names (Belgrad and Belçika) in my question's example to show the problem for two strings starting with the same letter. – alierdogan7 Jul 20 '23 at 12:54
  • Indeed, this is a very simple solution but I'm afraid Dart does not provide a locale-sensitive sorting. You can modify the comparison method to compare each character until one is different – lucie Jul 20 '23 at 14:25
  • @alierdogan7 I updated the comparison function above, which loops over all letters of the country – lucie Jul 20 '23 at 14:37
1

Although, Lucie's answer works, I ended up writing my own extension for strings for Turkish alphabet operations for the general use. The Turkish alphabet has a weird issue with capital letter "I". Its lowercase form is "ı" (not "i"). Also, the lowercase letter "i" has an uppercase form as "İ". Dart does not support this minor detail for the Turkish in its toLowerCase function.

Sorting strings with Turkish characters can be done with the snippet below. (based on Daniel's answer):

extension TurkishStringOperations on String {
  String toTurkishLowerCase() {
    return replaceAll("İ", "i")
        .replaceAll("Ş", "ş")
        .replaceAll("Ç", "ç")
        .replaceAll("Ö", "ö")
        .replaceAll("I", "ı")
        .replaceAll("Ü", "ü")
        .replaceAll("Ğ", "ğ")
        .toLowerCase();
  }

  String toTurkishUpperCase() {
    return replaceAll("i", "İ")
        .replaceAll("ş", "Ş")
        .replaceAll("ç", "Ç")
        .replaceAll("ö", "Ö")
        .replaceAll("ı", "I")
        .replaceAll("ü", "Ü")
        .replaceAll("ğ", "Ğ")
        .toUpperCase();
  }

  int turkishCompareTo(String other) {
      var letters = [
        "a", "b", "c", "ç", "d", "e", "f", "g", "ğ", "h", "ı", "i", "j", "k",
        "l", "m", "n", "o", "ö", "p", "r", "s", "ş", "t", "u", "ü", "v", "y",
        "z", "w", "q", "x",
      ];

      var that = toTurkishLowerCase();
      other = other.toTurkishLowerCase();

      for (var i = 0; i < min(that.length, other.length); i++) {
        var thatValue = letters.indexOf(that[i]);
        var otherValue = letters.indexOf(other[i]);

        var result = (thatValue - otherValue).sign;
        if (result != 0) {
          return result;
        }
      }

      return (that.length - other.length).sign;
  }
}

The example list below is sorted with a standard sort() method of list using our extension method:

var countries = ["Belgrad", "Belçika", "Irak", "İran", "Sudan", "Şili", "Çek Cumhuriyeti", "Cezayir", "Ukrayna", "Ürdün"];


countries.sort((firstString, secondString) => firstString.compareTo(secondString));
print(countries); // prints in a wrong order: [Belgrad, Belçika, Cezayir, Irak, Sudan, Ukrayna, Çek Cumhuriyeti, Ürdün, İran, Şili]

countries.sort((firstString, secondString) => firstString.turkishCompareTo(secondString));
print(countries); // prints in a correct Turkish order:  [Belçika, Belgrad, Cezayir, Çek Cumhuriyeti, Irak, İran, Sudan, Şili, Ukrayna, Ürdün]
alierdogan7
  • 600
  • 7
  • 14