olifolkerd / tabulator

Interactive Tables and Data Grids for JavaScript
http://tabulator.info
MIT License
6.48k stars 801 forks source link

Natural ordering sorter #4491

Closed opatry closed 2 months ago

opatry commented 2 months ago

It would be nice to have a built-in sorter respecting the natural ordering. The use case I have in mind is linked to numbers, 1, 2, 3…, 10, 20, 100 (instead of 1, 10, 100, 2, 3…). Another use case is the management of accented characters.

I went for a custom sorter in the meantime but I guess this would be valuable for many.

// sort taking into account locale (1, 2, 10 vs 1, 10, 2; accented characters etc.)
sorter: function(a, b, aRow, bRow, column, dir, sorterParams) {
  return a.localeCompare(b, 'fr', { numeric: true, sensitivity: 'base', ignorePunctuation: true });
}

Options of this sorter could simply expose String.prototype.localeCompare() options.

My personal case to showcase the benefit is to display a list of books, having "tome X" for each of them:

olifolkerd commented 2 months ago

If you are referring to the order the data was loaded in, that is the default behavior of the table if you don't sort it.

I'm unsure in what other case you would be referring to there

opatry commented 2 months ago

Sorry for the answer delay, I miss your answer notification last week…

Here is what I obtain with default sorting (or string sorting, or string sorting with locale)

(same is with nothing set, sorter: "string", sorter: "string", sorterParams: { locale : 'fr' }, or sorter: "string", sorterParams: { locale : true })

tabulator_string_sort

Here is what I obtain my custom sort relying on JS localeCompare

sorter: function(a, b, aRow, bRow, column, dir, sorterParams) {
  return a.localeCompare(b, 'fr', { numeric: true, sensitivity: "base", ignorePunctuation: true });
},
tabulator_natural_string_sort

Here is a self contained repro file with commented out alternatives l. 80

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <title>Tabulator natural sorting</title>

  <link href="https://unpkg.com/tabulator-tables@6.2.1/dist/css/tabulator.min.css" rel="stylesheet">
  <script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
</head>

<body>
  <div id="books-table"></div>

  <script type="text/javascript">
    var booksData = [
    { "title": "... Et mon tout est un homme", "author": "Blah" },
    { "title": "et hop", "author": "Test" },
    { "title": "Entre ciel et terre", "author": "Test" },
    { "title": "Et si c'était vrai", "author": "Other" },
    { "title": "Été", "author": "A" },
    { "title": "Ma série tome 1, mon titre", "author": "A" },
    { "title": "Ma série tome 2, mon titre", "author": "A" },
    { "title": "Ma série tome 3, mon titre", "author": "A" },
    { "title": "Ma série tome 10, mon titre", "author": "A" },
    { "title": "Ma série tome 111, mon titre", "author": "A" },
    { "title": "Ma série tome 200, mon titre", "author": "A" }
  ];
  var table = new Tabulator("#books-table", {
    data: booksData,
    layout: "fitColumns",
    pagination: "local",
    paginationSize: 18,
    paginationCounter: "rows",
    initialSort: [
        {
          column: "title",
          dir: "asc"
        },
    ],
    columns: [
        {
          headerSort: true,
          field: "title",
          title: "Titre",
          sorter: "string",
          // sorter: "string", sorterParams: { locale : true },
          // sort taking into account locale (1, 2, 10 vs 1, 10, 2; accented characters etc.)
          // sorter: function(a, b, aRow, bRow, column, dir, sorterParams) {
          //   return a.localeCompare(b, 'fr', { numeric: true, sensitivity: "base", ignorePunctuation: true });
          // },
        },
        {
          headerSort: true,
          field: "author",
          title: "Auteur",
          responsive: 1,
          width: 250,
          minWidth: 100,
          minWidth: 150,
        },
    ],
  });

  window.addEventListener("load", (event) => {
    table.setLocale("fr");
  });
  </script>
  </div>
</div>
</body>
</html>

Let me know if I missed your point.

opatry commented 2 months ago

And, indeed, if I remove the initialSort, I get the order coming from the data source, but this is not what I need.

olifolkerd commented 2 months ago

I'm afraid I still don't understand what it is you actually need what circumstances are you not able to get the default sort? Adding any sort to a column will naturally result in it being sorted

opatry commented 2 months ago

Then we are 2 not understanding the other 😅

I need to sort some columns using natural ordering (Javascript localeCompare()).

My assumption is that it's a legit need which would be beneficial to others, so having it built-in would be nice IMO. That being said I can use a custom sorter and we can stop here.

I don't get your point about "default sort", how is it linked to this need at all, I don't make the link.

Even if I sort the original data source using my own logic, it's defined for first time and for a single column, it's not dynamic…

So, nevermind, I thought it would be a no brainer, it isn't, I'll stick to my current impl which is fine.

olifolkerd commented 2 months ago

Yeah I think the challenge here is what you are referring to as natural ordering, which is not a specific term for search.

If you are literally referring to language specific string sorting, then that is specific to your usage case and is exactly why tabulator allows the addition of custom sort functions