Search code examples
flutterlistdartsortinghebrew

How can I sort a list alphabetically based on the Hebrew language in Dart?


Context

In Dart, if I have a list:

final myList = ['b', 'a'];

and I wanted to sort it alphabetically, I would use:

myList.sort(
      (String a, String b) => a.compareTo(b),
    );

The output of myList is now:

['a', 'b']

Now, this works on letters that are in the English alphabet.

Question

But if I have a list that's in Hebrew:

final unorderedHebAlphabet = ['א', 'ב'];

I can't sort it as above using with:

 unorderedHebAlphabet.sort((String a, String b) =>
        a.compareTo(b))

It doesn't sort.

Expected output, instead of:

['א', 'ב']

Should be:

['ב', 'א']
  • How can I sort a list Alphabetically in the Hebrew language?

Notes

As a reference, the Hebrew alphabet sorted would be in this order:

final sortedHebrewAlphabet = [
  'א',
  'ב',
  'ג',
  'ד',
  'ה',
  'ו',
  'ז',
  'ח',
  'ט',
  'י',
  'כ',
  'ל',
  'מ',
  'נ',
  'ס',
  'ע',
  'פ',
  'צ',
  'ק',
  'ר',
  'ש',
  'ת',
];

Solution

  • It does sort (by UTF-16 code units), but it's being shown in an unintuitive way. final unorderedHebAlphabet = ['א', 'ב']; seems to be parsed RTL, so in the constructed list, element 0 is א and element 1 is ב. That's already the desired order, so sorting it does not change it. (Mixing LTR and RTL text is confusing.)

    For example:

    import 'package:collection/collection.dart';
    
    void main() {
      var literal = ['א', 'ב'];
      print(literal[0]); // Prints: א
      print(literal[1]); // Prints: ב
    
      const alef = 'א';
      const bet = 'ב';
      const expectedOrder = [alef, bet];
    
      const listEquals = ListEquality<String>();
      
      print(listEquals.equals(literal..sort(), expectedOrder)); // Prints: true
      print(listEquals.equals([bet, alef]..sort(), expectedOrder)); // Prints: true
    }
    

    You also can observe that the elements are printed in the correct order if you prefix output with the Unicode LTR override (U+202D) to force rendering the text as LTR. Compare:

    const ltr = '\u202D';
    print('$expectedOrder');
    print('$ltr$expectedOrder');
    

    Or you could simply print the elements separately:

    expectedOrder.forEach(print);
    

    which prints:

    א
    ב
    

    I'm not experienced with dealing with RTL text, but I'd probably avoid mixing LTR and RTL text in code and instead express them as hexadecimal Unicode code points to avoid confusion.