Сортировка строк таблицы и производительность строк - PullRequest
4 голосов
/ 08 апреля 2009

У меня есть таблица с большим количеством строк, которая не подходит для подкачки. Строки в этой таблице можно отсортировать, щелкнув заголовок столбца, который запускает алгоритм сортировки на стороне клиента на основе 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 ]

5 голосов
/ 08 апреля 2009

В приведенном вами примере есть несколько проблем. Проблема номер один в том, что вы выбираете столбцы, используя 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 для доступа к столбцу, вам нужен индекс столбца, чтобы вы могли перебирать столбцы th, пока не найдете правильный (при условии, что форматирование html идентично для тегов th и тегов td). Затем вы можете использовать этот индекс для получения правильного дочернего узла строки.

Кроме того, если таблица является статической, и пользователи могут выполнять дополнительную сортировку, вы должны кэшировать строки и не удалять свойство sortKey. Тогда вы экономите около 30% времени сортировки. Существует также вопрос содержания таблицы. Если содержимое является текстом, этот метод сортировки в порядке. Если он содержит числа и т. Д., Вам следует учесть это, поскольку я использую localeCompare, который является методом типа String.

2 голосов
/ 08 апреля 2009

Одна оптимизация, о которой я могу подумать, это изменить этот код:

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

чтобы вместо добавления по одной строке за раз добавлять более крупные фрагменты или все, если это возможно. Для этого вам нужно сначала создать строку из всех строк, а затем добавить все сразу.

используйте array.push и array.join для конкатенации строки

1 голос
/ 08 апреля 2009

Еще одно улучшение производительности: Не используйте $ .each, но вместо этого используйте обычный цикл for, где вы можете помочь. обычные петли немного быстрее.

также этот блог может быть полезен, если не сейчас, то в будущем:

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

0 голосов
/ 09 апреля 2009

Я использовал для этой работы плагин DataTables jQuery , а ранее я использовал компонент YUI DataTable . Бета-версия 1.5 компонента jQuery DataTables включает в себя либо создание экземпляра из HTML-кода, либо использование источника данных AJAX. Это довольно просто для настройки, а для просеивания и сортировки данных это было очень быстро.

Я обнаружил, что после того, как набрал около 1600 строк своего довольно многословного, единственный способ был - загрузить данные на сервер.

0 голосов
/ 08 апреля 2009

Следуя предложению mkoryak, я изменил код, чтобы собрать строки в массив, а затем добавить их все за один раз:

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

Это, похоже, улучшило производительность примерно на 25% - сортировка 1500 строк теперь занимает ~ 3 с, а не ~ 4 с ранее.

0 голосов
/ 08 апреля 2009

Тьена Ола!

Возможно, это даже не связано с вашим вопросом, но о скольких строках мы говорим? Для очень большого количества строк - десятки тысяч - таблицы ужасно медленные. Более легким решением было бы использовать div и css для имитации таблиц. Возможно, он не является семантически правильным (ха! Таблицы и семантика в одной фразе, кто бы мог подумать?), Но это НАМНОГО быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...