【问题标题】:Sorting an Observable Array in Knockout在 Knockout 中对 Observable 数组进行排序
【发布时间】:2023-03-21 05:39:01
【问题描述】:

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

<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);
    });    
});

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

Joe Bailey
Jack Brown
Adam Brown
Bob Brown
Jim Byrd

我希望对重复的姓氏也进行排序:

Joe Bailey
Adam Brown
Bob Brown
Jack Brown
Jim Byrd

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

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

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

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

    也就是说,您要求的是按两个数据排序,姓氏,然后是名字。

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

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

    我敢肯定,这会更有效率。基本上你有三个条件在起作用:

    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) })"
    

    当然,当您引入更多排序向量(例如反转或附加数据)时,这可能会变得笨拙,因此您应该实现一个过滤器函数来为您执行上述评估:

    data-bind="foreach: items().sort(my.utils.compareItems)"
    

    【讨论】:

    • 我也有同样的问题,感谢您的回答。我能够使用过滤器函数方法对数组进行排序。但是,我发现如果数据被更新(它是一个可观察的),该数组不会自动使用。我做错了什么?
    • 参见这个小提琴jsfiddle.net/RbX86 和这个小提琴jsfiddle.net/RbX86/1 - 这首先展示了如何将 sort() 与绑定分开,第二个展示如何启动重新绑定/重新- 在数据更改后评估 observableArray(通过valueHasMutated()) - 注意可观察数组在第一个小提琴中是如何排序的,而底层数组(不是可观察数组)在第二个示例中是如何排序的。这些小提琴来自stackoverflow.com/questions/10480118/…
    • 对我有用,但我必须使用 items().sort(...) 而不是 items.sort(...)
    • 我发现使用 items.sort 显示初始列表效果很好,但是如果我将新项目推送到可观察数组,除非我使用 items().sort(),否则它不会显示,所以对Fabrice的建议+1,应该更新原始答案。
    • 我最近在 Github 中打开了一个 issue for Knockout 以更好地处理排序情况:github.com/knockout/knockout/issues/1380
    【解决方案2】:

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

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

    players.sort(function(player1, player2) {
        return player1.lastname.localeCompare(player2.lastname) ||
            player1.firstname.localeCompare(player2.firstname);
    });
    

    【讨论】:

    • 实际上,我通过一个 Web 服务来获取球员,在该服务中他们按球队进行排序。我的问题是我还允许用户按位置、点数和其他属性对玩家进行排序,因此在这种情况下,能够按姓氏正确地使用名字很重要。
    • 您的代码不包含任何排序。那么他们是如何按姓氏排序的呢?无论如何,我添加了一个使用 Javascript 按姓氏和名字排序的示例。如果您还需要什么,请告诉我。
    • 有什么更新吗?如果这有助于您解决问题,请接受。如果没有,请告诉我还有什么情况。
    【解决方案3】:

    我在尝试对可观察数组进行排序时遇到了一些问题,我在代码中没有看到任何结果。

    您需要先对通过 ajax/getJSON 请求收到的结果进行排序,然后再将新项目返回到数组中。

    通过这种方式,在将结果作为新项目添加到您的可观察数组之前对其进行排序。那么就不需要在绑定处对它们进行排序了。

    请参见下面的示例。

    players(ko.utils.arrayMap(result, function (item) {
                        result.sort(function (l, r) {
                            return l.name == r.name ? 0 : (l.name < r.name ? -1 : 1);
                        });
                    return new Player(item);
                }));
    
    1. 使用 AJAX / getJSON 获取数据
    2. 将该请求的结果排序到您选择的数组中

    【讨论】:

    • 这种方法的唯一问题是在客户端引入新数据会导致未排序的结果,因此需要手动重新排序以响应可观察数组的变化——这会破坏一些价值在使用 ko.observableArray 时——虽然,是的,JavaScript sort() 确实对数组进行排序——还值得注意的是,原始问题的一部分不是绑定方法,而是需要对多个范围进行排序(OP 仅按最后一个排序姓名,当多人具有相同的姓氏时,会导致不确定/自然顺序 - 即亚当、鲍勃和杰克。)
    【解决方案4】:

    您必须使用两次排序来执行此操作。在第一次排序中,根据人的姓氏进行排序,在第二次排序中,如果有两个以上的姓氏相同,则根据姓氏进行搜索,而不是根据他们的名字进行排序在他们的位置,使用一些标准排序算法......

    【讨论】:

    • 对不起,小声,但这不正确或有启发性。
    猜你喜欢
    • 2014-03-02
    • 2015-01-17
    • 1970-01-01
    • 2019-09-06
    • 2013-07-17
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多