在Knockout中对可观察数组进行排序

use*_*156 64 knockout.js

我在人物对象的Knockout中有一个可观察的数组.我希望能够根据姓氏对人员列表进行排序.问题是该列表有许多重复的姓氏.结果是,当有多个姓氏时,名字会在找到时显示.我希望能够按姓氏对数组进行排序,并且当有多个姓氏时,还要按名字排序.我正在使用文本输入让用户开始输入姓氏.结果绑定到显示所有匹配项的模板.

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

这是我的Knockout数组过滤器代码:

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);
    });    
});
Run Code Online (Sandbox Code Playgroud)

对于姓氏过滤,这工作正常,但是当有重复的姓氏时,我无法找到添加排序名字的方法.例如,在我的数组中按姓氏排序时我得到:

Joe Bailey
Jack Brown
Adam Brown
Bob Brown
Jim Byrd
Run Code Online (Sandbox Code Playgroud)

我希望重复的姓氏也可以排序他们的名字:

Joe Bailey
Adam Brown
Bob Brown
Jack Brown
Jim Byrd
Run Code Online (Sandbox Code Playgroud)

Sha*_*son 102

KnockoutJS可观察数组提供了一个排序函数,这使得在UI中对数据绑定数组进行排序相对容易(无论数据的自然顺序如何.)

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

您不必对源数据进行预排序/重新排序,如果您在绑定之前进行排序(或由于排序而重新绑定),那么您做错了.

那就是说,你要问的是按两个数据,姓氏,然后是名字排序.

而不是"l.lastName()> r.lastName()?1:-1"而不是考虑以下内容:

l.lastName() === r.lastName() 
    ? l.firstName() > r.firstName() ? 1 : -1
    : l.lastName() > r.lastName() ? 1 : -1
Run Code Online (Sandbox Code Playgroud)

我敢肯定,这可能更有效率.基本上你有三个条件:

  1. 姓氏是否相等?
  2. 如果是这样,请比较名字并返回结果.
  3. 否则,比较姓氏并返回结果.

我已经扫描了你的代码,我发现没有这样的排序功能.

这与Michael Best的答案类似,但是,我试图澄清WHERE来处理排序(在你的绑定中,第一个例子)和如何实现你正在寻找的排序(多个数据).

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) })"
Run Code Online (Sandbox Code Playgroud)

当然,当您引入更多排序向量(例如反转或其他数据)时,这可能变得难以处理,因此您应该实现一个过滤函数,为您执行上述评估:

data-bind="foreach: items().sort(my.utils.compareItems)"
Run Code Online (Sandbox Code Playgroud)

  • 适合我,但我不得不使用items().sort(...)而不是items.sort(...) (17认同)
  • 我发现使用items.sort显示初始列表工作正常,但是如果我将一个新项目推送到observable数组,它将不会显示,除非我使用items().sort(),所以+1给Fabrice的建议,并且原始答案应该更新. (3认同)
  • 我最近在Github上为Knockout打开了一个问题,以便更好地处理排序情况:https://github.com/knockout/knockout/issues/1380 (3认同)
  • 我有同样的问题,谢谢你的回答.我能够使用过滤函数方法对我的数组进行排序.但是,我发现如果数据被更新(这是一个可观察的数据),数组将不会自动求助.我究竟做错了什么? (2认同)

Mic*_*est 3

如果您确保players.json返回的名称已排序,那就没问题了。如果它从数据库加载它们,您需要将名字字段添加到您的ORDER BY子句中。

如果您想在 Javascript 中进行排序,您可以在从服务加载后立即进行:

players.sort(function(player1, player2) {
    return player1.lastname.localeCompare(player2.lastname) ||
        player1.firstname.localeCompare(player2.firstname);
});
Run Code Online (Sandbox Code Playgroud)