Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting an Observable Array in Knockout

Tags:

knockout.js

I have an observable array in Knockout of person objects. I wanted to be able to sort the list of persons based on the last name. The problem is the list has a number of duplicate last names. The result is that when there are more than one last name, the first names appear as they were found. I'd like to be able to sort the array by last name and when there are more than one last name, have it also sort by first name. I'm using a text input for the user to start typing in the last name. The results are bound to a template that displays all the matches.

<input data-bind="value: filter, valueUpdate: 'afterkeydown'">

And here is my Knockout array filter code:

function Item(firstname, lastname) {      this.firstname = ko.observable(firstname);      this.lastname = ko.observable(lastname); }  var playersViewModel = {      items: ko.observableArray([]),      filter: ko.observable("") }; var players;  $(function() {     playersViewModel.filteredItems = ko.computed(function() {          var filter = this.filter().toLowerCase();          if (!filter) {               return this.items();          } else {               return ko.utils.arrayFilter(this.items(), function(item) {                     return ko.utils.stringStartsWith(item.lastname().toLowerCase(), filter);               });          }     }, playersViewModel);      $.getJSON('./players.json', function(data) {         players = data.players;         playersViewModel.players = ko.observableArray(players);         ko.applyBindings(playersViewModel);             var mappedData = ko.utils.arrayMap(players, function(item) {              return new Item(item.firstname,item.lastname);         });         playersViewModel.items(mappedData);     });     }); 

For last name filtering this works fine, but I'm unable to find a way to add in sorting first name when there are duplicate last names. For example, in my array when sorted by last name I'm getting:

Joe Bailey Jack Brown Adam Brown Bob Brown Jim Byrd 

I'd like the duplicate last names to have their first names sorted as well:

Joe Bailey Adam Brown Bob Brown Jack Brown Jim Byrd 
like image 433
user1715156 Avatar asked Oct 04 '12 00:10

user1715156


People also ask

What is the function used to search or sort an observable array The configuration file are stored in following folder?

The Sort function is used to sort the data present in the Array, in the sorted Array we pass another function that returns the value by comparing them.

How do you remove data from an observable array?

shift() — Removes the first value from the array and returns it. reverse() — Reverses the order of the array and returns the observableArray (not the underlying array). sort() — Sorts the array contents and returns the observableArray .

What is Ko observable ()?

An observable is useful in various scenarios where we are displaying or editing multiple values and require repeated sections of the UI to appear and disappear as items are inserted and deleted. The main advantage of KO is that it updates our UI automatically when the view model changes.


1 Answers

KnockoutJS observable arrays offer a sort function, this makes it relatively easy to sort a databound array in your UI (regardless of the natural order of the data.)

data-bind="foreach: items.sort(function (l, r) { return l.lastName() > r.lastName() ? 1 : -1 })" 

You should not have to pre-sort/re-sort your source data, if you're sorting before binding (or rebinding due to a sort) then you're doing it wrong.

That said, what you're asking is to sort by two datum, last name, then first name.

Instead of "l.lastName() > r.lastName() ? 1 : -1" consider the following:

l.lastName() === r.lastName()      ? l.firstName() > r.firstName() ? 1 : -1     : l.lastName() > r.lastName() ? 1 : -1 

This could be more efficient, I'm sure. Basically you have three conditionals at play:

  1. Are the last names Equal?
  2. If so, compare first names and return a result.
  3. Else, compare last names and return a result.

I've scanned your code and I see no such sort function in place.

This is similar to Michael Best's answer, however, I've attempted to clarify WHERE to handle sorting (in your binding, first example) and HOW to achieve the sort you're looking for (multiple datum.)

data-bind="foreach: items().sort(function (l, r) { return (l.lastName() == r.lastName()) ? (l.firstName() > r.firstName() ? 1 : -1) : (l.lastName() > r.lastName() ? 1 : -1) })" 

Naturally, this can get unwieldy as you introduce more sort vectors, such as reversals or additional datum, and so you should implement a filter function that performs the above evaluation for you:

data-bind="foreach: items().sort(my.utils.compareItems)" 
like image 130
Shaun Wilson Avatar answered Sep 29 '22 11:09

Shaun Wilson