-1

Hello i am new in knockout javascript, I have a observablearray conataining json objets. So i am trying to sort the list of objets by a given order.

Ideclare the observablearray like yhis :

 var self =this;
 self.array= ko.observableArray();

i loop over the observablearray named "array" with this code:

self.array().forEach(function(v,i){
 alert(JSON.stringify(v))
}

its returns me:

{"uuid":"74af2d36-aa47-45c5-af5d-b32c8ed56202","label":"a"}
{"uuid":"412f6222-e4c3-40a0-8a30-b1e31f53d746","label":"b"}
{"uuid":"115c9fa4-c43b-4ad0-bad7-855da850905f","label":"c"}
{"uuid":"55671032-9fc5-4361-8722-3d14abaa7d81","label":"d"}

I really want to have something like this into my new observableArray:

{"uuid":"115c9fa4-c43b-4ad0-bad7-855da850905f","label":"c"}
{"uuid":"74af2d36-aa47-45c5-af5d-b32c8ed56202","label":"a"}
{"uuid":"55671032-9fc5-4361-8722-3d14abaa7d81","label":"d"}
{"uuid":"412f6222-e4c3-40a0-8a30-b1e31f53d746","label":"b"}
solo
  • 338
  • 4
  • 16
  • The expected output is neither sorted based on `uuid` nor `label` (The current array you have, however, is sorted based on `label`) – adiga May 15 '19 at 10:50
  • https://stackoverflow.com/questions/12718699/sorting-an-observable-array-in-knockout – Ray May 15 '19 at 11:09

1 Answers1

0

JS Sorting 101

To sort an array you use Array.prototype.sort. You pass it a function that takes two items, a and b, and returns either 1, -1 or 0.

If it returns 1 if a comes after b, -1 if a comes before b, and 0 if there's no difference.

const stringSort = (a, b) => a.localeCompare(b);
const numericSort = (a, b) => a > b ? 1 : a < b ? -1 : 0;

const products = [
  { name: "Bread",       cost: 3.10 },
  { name: "Almond Milk", cost: 1.30 },
  { name: "Chocolate",   cost: 2.90 }
];

console.log(
  "By name:", 
  products.sort((p1, p2) => stringSort(p1.name, p2.name))
);

console.log(
  "By cost:", 
  products.sort((p1, p2) => numericSort(p1.cost, p2.cost))
);

Notice that you can't pass the helpers directly to the products.sort method! You need another function that defines by which property we want to sort.

Using knockout

The cool thing with knockout is that you can create a sorted version of your data that automatically updates whenever the source changes! Here's an interactive example to show what you can do.

In the example below:

  • I sort inside a computed so that the UI's table updates automatically
    • when you add a product
    • when you remove a product
    • when you change the sorting method
    • when you change the sorting direction

const stringCompare = (a, b) => a.localeCompare(b);
const numericCompare = (a, b) => a > b ? 1 : a < b ? -1 : 0;

const products = ko.observableArray([
  { name: "Bread",       cost: 3.10 },
  { name: "Almond Milk", cost: 1.30 },
  { name: "Chocolate",   cost: 2.90 }
]);

const sorters = [
  { 
    label: "By cost",
    sort: (a, b) => numericCompare(a.cost, b.cost)
  },
  { 
    label: "Alphabetically",
    sort: (a, b) => stringCompare(a.name, b.name)
  }
];

const desc = ko.observable(true);

const sorter = ko.observable(sorters[0]);
const sortMethod = ko.pureComputed(() => desc() 
  ? (a, b) => -1 * sorter().sort(a, b) 
  : sorter().sort
)

const sortedProducts = ko.pureComputed(() =>
  products().sort(sortMethod())
);

const newProduct = {
  name: ko.observable(""),
  cost: ko.observable(0)
};

const removeProduct = p => {
  products.remove(p);
}

const addProduct = () => {
  products.push({ 
    name: newProduct.name(),
    cost: +newProduct.cost() 
  });
  
  newProduct.name("");
  newProduct.cost(0);
};


ko.applyBindings({ sorters, desc, sortedProducts, newProduct, addProduct, removeProduct })
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<div>
  Sort by: <select data-bind="options: sorters, value: sorter, optionsText: 'label'"></select> 
  Descending <input type="checkbox" data-bind="checked: desc">
</div>

<table>
  <thead>
    <tr>
      <th>no.</th>
      <th>Name</th>
      <th>Cost</th>
      <th></th>
    </tr>
  </thead>
  <tbody data-bind="foreach: sortedProducts">
    <tr>
     <td data-bind="text: $index() + 1"></td>
     <td data-bind="text: name"></td>
     <td data-bind="text: '$' + cost.toFixed(2)"></td>
     <td>
       <button data-bind="click: $root.removeProduct">remove</button>
     </td>
    </tr>
  </tbody>
  <tbody data-bind="with: newProduct">
    <tr>
      <td></td>
      <td>
         <input data-bind="value: name" placeholder="product name">
      </td>
      <td>
        <input type="number" min="0" step="0.1" data-bind="value: cost">
       </td>
       <td>
         <button data-bind="click: addProduct">add</button>
       </td>
     </tr>
  </tbody>

</table>
user3297291
  • 22,592
  • 4
  • 29
  • 45