Search code examples
angulartypescriptsortingangular-materialcollation

Collation for MatSort with languages where alphabetical order is different from Unicode order?


In Norwegian and Danish Alphabets, the correct order of the Norwegian/Danish characters are:

  1. Æ
  2. Ø
  3. Å

However, MatSort uses the Unicode order for these characters:

  1. Å (197)
  2. Æ (198)
  3. Ø (216)

Could collation be implemented somehow to solve this?

Here is a stackblitz with a table that can be sorted by "No." and "Name":

https://stackblitz.com/edit/angular-an1uqc-8mdqns

Here's the data in the table:

 {position: 1, name: 'Alpha', description: 'Test123'},
  {position: 2, name: 'Bravo',  description: '0'},
  {position: 3, name: 'Charlie', description: 'aaa'},
  {position: 4, name: 'Delta',  description: ''},
  {position: 5, name: 'Echo',  description: '1'},
  {position: 6, name: 'Foxtrot',  description: '2'},
  {position: 7, name: 'ÆGamma',  description: ''},
  {position: 8, name: 'ØHotel',  description: '3'},
  {position: 9, name: 'ÅIndigo',  description: '1000'},

];

The correct way to sort the last three items (ÆGamma, ØHotel and ÅIndigo) according to the Norwegian/Danish alphabet would be:

  1. ÆGamma
  2. ØHotel
  3. ÅIndigo

But MatSort uses the Unicode numbers for these characters and sorts like this instead:

  1. ÅIndigo (197)
  2. ÆGamma (198)
  3. ØHotel (216)

Thanks for reading! :]


Solution

  • You would need to use matSortChange to implement a custom sort, in this case a localized sort. This can be done using String.prototype.localCompare with language identifier da-DK:

    Template:

    <table
      mat-table [dataSource]="sortedData"
      matSort (matSortChange)="sortData($event)"
      class="mat-elevation-z8">
    

    Component:

    sortData(sort: Sort) {
      const data = ELEMENT_DATA.slice();
      if (!sort.active || sort.direction === '') {
        this.sortedData = data;
        return;
      }
    
      this.sortedData = data.sort((a, b) => {
        const isAsc = sort.direction === 'asc';
        switch (sort.active) {
          case 'position': return compareNumber(a.position, b.position, isAsc);
          case 'name': return compareString(a.name, b.name, isAsc);
          case 'description': return compareString(a.description, b.description, isAsc);
          default: return 0;
        }
      });
    }
    
    // ...
    
    function compareNumber(a: number, b: number, isAsc: boolean) {
      return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
    }
    
    function compareString(a: string, b: string, isAsc: boolean) {
      return a.localeCompare(b, 'da-DK') * (isAsc ? 1 : -1);
    }
    

    Here is an example in action.

    The sorted results are as follows:

    1 Alpha
    // ...
    6 Foxtrot
    7 ÆGamma
    8 ØHotel
    9 ÅIndigo
    

    Then in reverse:

    9 ÅIndigo
    8 ØHotel
    7 ÆGamma
    6 Foxtrot
    // ...
    1 Alpha
    

    Hopefully that helps!