Sort oberservableArray by multiple parameters

329 Views Asked by At

I am trying to sort by two items of an obervableArray. I have created a simplified version of this at http://jsfiddle.net/organa7/yyy3y2ka/.

Line Group Id
<select data-bind="options: lineGroupIds, optionsValue: 'value', optionsText: 'text', value: selectedGroupId">
</select>

Line Type
<select data-bind="options: lineTypes, optionsValue: 'value', optionsText: 'text', value: selectedLineType">
</select>

<button data-bind="click: addLine">Add Line</button>
</br>
Line Count:&nbsp;<span data-bind="text: lines().length"></span>
</br>
<table> 
     <thead>
         <tr>
             <th>Line Group Id</th>
             <th>Line Type</th>
         </tr>
     </thead>
<tbody data-bind='foreach: linesSorted'>
    <tr>
         <td>
            <span data-bind="text: lineGroupId"></span>
        </td>
        <td>
            <span data-bind="text: lineType"></span>
        </td>
    </tr>
</tbody>
</table>
function myViewModel() {
    var self = this;

    self.selectedLineType = ko.observable(2);
    self.selectedGroupId = ko.observable(2);

    self.lines = ko.observableArray([
        {lineType: 1, lineGroupId: 1}, 
        {lineType: 1, lineGroupId: 2}, 
        {lineType: 1, lineGroupId: 3},

        /*{lineType: 2, lineGroupId: 1}, 
        {lineType: 3, lineGroupId: 1}, 
        {lineType: 2, lineGroupId: 1}, 
        {lineType: 2, lineGroupId: 2}, 
        {lineType: 3, lineGroupId: 2}, 
        {lineType: 2, lineGroupId: 1},*/
    ]);

    self.linesSorted = ko.pureComputed(function () {
        return self.lines().sort(function (a, b) {
            return a.lineType == b.lineType ?
            0 :
            (a.lineType < b.lineType ? -1 : 1);
        }).sort(function (a, b) {
            return a.lineGroupId == b.lineGroupId ?
            0 :
            (a.lineGroupId < b.lineGroupId ? -1 : 1);
        })
    });    

    self.lineTypes = ko.observableArray([
        {value: 2, text: "2"},
        {value: 3, text: "3"}
    ]);

    self.lineGroupIds = ko.observableArray([
        {value: 1, text: "1"},
        {value: 2, text: "2"},
        {value: 3, text: "3"}
    ]);

    self.addLine = function () {
        self.lines.push({
            lineType: self.selectedLineType(),
            lineGroupId: self.selectedGroupId()
        });
    };
}

ko.applyBindings(new myViewModel());

They should be sorted by LineGroupId and then by LineType. For each LineGroupId there should be one and only one of LineType 1. This is the parent item. I have added a multiple sort to the lines that are bound to the table. It works fine until the number of lines goes over 10: then you get a lineType of 2 or 3 above lineType of 1. Any help on what is going on here would be greatly appreciated.

2

There are 2 best solutions below

0
On

Sounds like they're sorting textually instead of numerically. Use a numeric comparison function (see parameter example here). Also, to sort by two fields, don't sort twice, do both comparisons in the same compare function. This ought to get you sorted (sorry):

function compareNumeric (a, b){return a-b}

self.linesSorted = ko.pureComputed(function () {
    return self.lines().sort(function (a, b) {
        return compareNumeric(a.lineType, b.lineType) ||  compareNumeric(a.lineGroupId, b.lineGroupId);
    })
});
1
On

You're sorting not the way you want to.

  • What you seem to want: sort by Line Type first, then by Line Group Id.
  • What you are in fact doing: sort by Line Type, then sort the result again by Line Group Id.

Here's an updated version of your sorting function:

self.linesSorted = ko.pureComputed(function () {
    return self.lines().sort(function (a, b) {
        if (a.lineGroupId > b.lineGroupId) { return 1; }
        if (a.lineGroupId < b.lineGroupId) { return -1; }
        if (a.lineType > b.lineType) { return 1; }
        if (a.lineType < b.lineType) { return -1; }
        return 0;
    })
}); 

See this jsfiddle for a demo.

PS. I recommend writing unit tests for this kind of logic. See for example my somewhat related question about elegant "ThenBy" sorting in JavaScript on CodeReview.SE.