BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
856 stars 130 forks source link

Locale sorting #411

Closed boily closed 5 years ago

boily commented 5 years ago

Hi, item names with accents are not sorted correctly. ["école", "professeur"] sort in wrong way. I have tried to replace with return a.v.localeCompare(b.v) > 0 ? reverse : b.v.localeCompare(a.v) > 0 ? -reverse : 0; in the mapped.sort function and it works fine. thanks Roger

BorisMoore commented 5 years ago

Hi Roger,

localeCompare is unfortunately much slower than regular comparison operators, so it would not be a good idea to replace the default sort using your suggested code above.

The recommended approach is to use a custom sort function with localeCompare.

The code below is for a new sample which I will add to the jsviews.com site, showing the use of localeCompare in a custom sort function:

<!DOCTYPE html>
<!-- To run the current sample code in your own environment, copy this to an html page. -->

<html>
<head>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://www.jsviews.com/download/jsrender.min.js"></script>
  <link href="https://www.jsviews.com/samples/samples.css" rel="stylesheet" />
</head>
<body>

<script id="myTmpl" type="text/x-jsrender">
<div class="left">
  <label>Localized sort of French words</label>
  <ul>
    {{for words sort=~locale}} {{!-- Sort using a custom helper function with localeCompare() --}}
      <li>{{:}}</li>
    {{/for}}
  </ul>
</div>

<div class="left">
  <label>Multilevel sort</label>
  <ul>
    {{for people sort=~multilevel}}  {{!-- Sort using a custom helper function for multi-level sorting --}}
      <li>{{:name}}: ({{:details.role}}) &ndash; age {{:details.age}}</li>
    {{/for}}
  </ul>
</div>
</script>

<div id="page"></div>

<script>
// Custom sort functions
function localeSort(a, b) {
  // Return 1, -1 or 0 to specify relative position of `a` and `b` in the sort order
  // Localized sort
  return a.localeCompare(b) > 0 ? 1 : b.localeCompare(a) > 0 ? -1 : 0;
}

function multilevelSort(a, b) {
  // Return 1, -1 or 0 to specify relative position of `a` and `b` in the sort order
  // Sort by role, then by age (descending) then by name
  return level(a.details.role.toLowerCase(), b.details.role.toLowerCase()) // by role
      || level(b.details.age, a.details.age)  // by age
      || level(a.name.toLowerCase(), b.name.toLowerCase()); // by name
}

// Helper function for multi-level sort
function level(aField, bField) {
  return aField > bField ? 1 : aField < bField ? -1 : 0;
}

$.views.helpers({
    locale: localeSort,
    multilevel: multilevelSort
  });

var myTmpl = $.templates("#myTmpl"),

  data = {
    words:
      ["maître", "âme", "école", "amour", "absolu",
      "maison", "vôtre", "être", "effort"],
    people:
      [
        {name: "Bill", details: {age: 22, role: "Lead"}},
        {name: "Anne", details: {age: 32, role: "Assistant"}},
        {name: "Emma", details: {age: 19.1, role: "Team member"}},
        {name: "Jeff", details: {age: 33.5, role: "Lead"}},
        {name: "Xavier", details: {age: 32, role: "Team member"}},
        {name: "Julia", details: {age: 18, role: "Assistant"}},
        {name: "Bill", details: {age: 32, role: "Team member"}}
      ]
    },

  html = myTmpl.render(data);

$("#page").html(html);
</script>

</body>
</html>
boily commented 5 years ago

Thanks a lot Boris, very useful.