代码之家  ›  专栏  ›  技术社区  ›  Ola Tuvesson

表行排序和字符串性能

  •  4
  • Ola Tuvesson  · 技术社区  · 17 年前

    我有一张表,表中有大量不适合分页的行。此表中的行可以通过单击列标题进行排序,该列标题将触发基于以下内容的客户端排序算法 http://www.exforsys.com/tutorials/jquery/jquery-basic-alphabetical-sorting.html 函数动态地向每一行添加一个“expando”属性,从而缓存键预排序:

    row.sortKey = $(row).children('td').eq(column).text().toUpperCase();
    

    如您所见,属性值只是设置为单击的列的内容,一旦排序完成,这些值就会被丢弃(为空)。实际上,性能非常好,但是包含更多文本的列的排序速度似乎较慢。

    由于排序只是为了让用户更容易找到他们正在查找的行,所以可以通过使用SUBSTR(0,7)或其他内容(八个字符应提供足够的精度)来加快速度。但是,我发现执行substr()比它节省的性能成本要高,而且如果有任何原因,它会使排序速度变慢。

    有人知道可以应用于此方法的任何(其他)优化吗?

    下面是一个更完整的例子:

    var rows = $table.find('tbody > tr').get();
    $.each(rows, function(index, row) {
        row.sortKey = $(row).children('td').eq(column).text().toUpperCase()
    })
    rows.sort(function(a, b) {
        if (a.sortKey < b.sortKey) return -1
        if (a.sortKey > b.sortKey) return 1
        return 0
    })
    $.each(rows, function(index, row) {
        $table.children('tbody').append(row)
        row.sortKey = null
    })
    

    编辑:这里是我的代码的最终版本,包含了下面答案中提供的许多优化:

    $('table.sortable').each(function() {
        var $table = $(this);
        var storage = new Array();
        var rows = $table.find('tbody > tr').get();
        $('th', $table).each(function(column) {
            $(this).click(function() {
                var colIndex = this.cellIndex;
                for(i=0;i<rows.length;i++) {
                    rows[i].sortKey = $(rows[i].childNodes[colIndex]).text().toUpperCase();
                }
                rows.sort(function(a, b) {
                    if (a.sortKey < b.sortKey) return -1;
                    if (a.sortKey > b.sortKey) return 1;
                    return 0;
                });
                for(i=0;i<rows.length;i++) {
                    storage.push(rows[i]);
                    rows[i].sortKey = null;
                }
                $table.children('tbody').append(storage);
            });
        });
    });
    
    6 回复  |  直到 17 年前
        1
  •  2
  •   mkoryak    17 年前

    我能想到的一个优化是修改此代码:

    $.each(rows, function(index, row) {
            $table.children('tbody').append(row)
            row.sortKey = null
    })
    

    为了代替一次追加一行,追加更大的块,或者尽可能全部追加。要做到这一点,您需要首先创建一个包含所有行的字符串,然后一次附加所有行。

    使用array.push和array.join来concat字符串

        2
  •  5
  •   Helgi    17 年前

    您给出的示例有几个问题。第一个问题是在循环中使用jquery选择列。这是一个重大的性能惩罚。如果您对HTML代码有任何控制权,我建议您使用普通的DOM方法来获得想要排序的列。注意,有时当您可能期望一个表单元节点时,您可能会得到一个文本节点。我稍后再谈。for循环速度更快,因此您可以考虑使用它而不是$each,但我建议您对其进行基准测试。

    我以您的示例为例,创建了一个包含1000行的表。我用了大约750毫秒在我的机器上排序。我做了一些优化(见下面的代码),并设法把它降到200毫秒,排序本身需要20毫秒左右(不错)。

    var sb = [];
    sb.push("<table border='1'>");
    var x;
    for (var i = 0; i < 1000; i++) {
        x = Math.floor(Math.random() * 1000);
        sb.push("<tr><td>data");
        sb.push(x);
        sb.push("</td></tr>");
    }
    sb.push("</table>");
    
    document.write(sb.join(""));
    
    $table = $("table");
    var rows = $table.find('tbody > tr').get();
    var columnIndex = 0;
    
    var t = new Date();
    
    $.each(rows, function(index, row) {
        row.sortKey = $(row.childNodes[columnIndex]).text();
    });
    alert("Sort key: " + (new Date() - t) + "ms");
    t = new Date();
    
    rows.sort(function(a, b) {
            return a.sortKey.localeCompare(b.sortKey);
    });
    alert("Sort: " + (new Date() - t) + "ms");
    t = new Date();
    var tbody = $table.children('tbody').get(0);
    
    $.each(rows, function(index, row) {
        tbody.appendChild(row);
        delete row.sortKey;
    })
    
    alert("Table: " + (new Date() - t) + "ms");
    

    当你为速度而写的时候,你希望每个迭代都尽可能快,所以不要在循环中做你可以在循环之外做的事情。例如,移动$table.children(“tbody”).get(0);在最后一个循环之外,会极大地加快速度。

    至于使用dom方法访问列,您需要的是列索引,这样您就可以迭代第列,直到找到正确的列为止(前提是第个标记和td标记的HTML格式相同)。然后可以使用该索引获取正确的行子节点。

    此外,如果表是静态的,并且用户有可能对其进行更多排序,那么应该缓存行,而不是删除sortkey属性。然后您可以节省大约30%的排序时间。还有表格内容的问题。如果内容是文本,那么这种排序方法就可以了。如果它包含数字等,那么您需要考虑一下,因为我使用的是localecompare,这是一种字符串类型的方法。

        3
  •  1
  •   mkoryak    17 年前

    另一个性能改进: 不要使用$.每个,而是使用一个常规的for循环来代替它。规则的循环速度稍快。

    此外,如果不是现在,这个博客文章可能会很有用,然后在将来:

    http://notetodogself.blogspot.com/2009/02/building-fast-jquery-plugins.html

        4
  •  0
  •   Ola Tuvesson    17 年前

    根据mkoryak的建议,我现在修改了代码,将行组装到一个数组中,然后一次附加它们:

    $.each(rows, function(index, row) {
        storage.push(row);
        row.sortKey = null;
    });
    $table.children('tbody').append(storage);
    

    这似乎提高了性能,大约25%——与之前的~4s相比,1500行的排序现在需要~3s。

        5
  •  0
  •   artlung    17 年前

    我在使用 DataTables jQuery plugin 对于这种工作,我以前用过 YUI DataTable 组件。jquery datatables组件的1.5测试版包括从HTML代码实例化,或者使用Ajax数据源。它对于设置非常简单,对于筛选和排序数据来说非常快速。

    我发现,一旦我输入了大约1600行相当冗长的内容,唯一的方法就是加载服务器端的数据。

        6
  •  -4
  •   Amin Amini    17 年前

    Tjena Ola!

    这甚至可能与您的问题无关,但我们讨论了多少行?对于大量的行(数万张表)来说,速度非常慢。更轻量的解决方案是使用DIV和CSS来模拟表。它在语义上可能不正确(哈!同一短语中的表和语义,谁会想到呢?)但速度要快得多。