From 406a389d7cdecc61b97e24cd14b700ee46e888f3 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello <8296030+Piccirello@users.noreply.github.com> Date: Sat, 14 Jun 2025 06:06:33 -0700 Subject: [PATCH] WebUI: Improve performance of re-sorting table rows This change drastically improves the performance of changing a table's sorted column. This performance is achieved through improved data structures, namely removing operations that repeatedly spliced an array. We also no longer iterate over a potentially large array. On a torrent with ~50,000 files, re-rendering after a sort improves from ~20 seconds to 2 seconds. PR #22827. --- src/webui/www/private/scripts/dynamicTable.js | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index b0fa1a02e..8f5034be9 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -858,42 +858,49 @@ window.qBittorrent.DynamicTable ??= (() => { } else { const trs = [...this.getTrs()]; + const trMap = new Map(trs.map(tr => [tr.rowId, tr])); for (let rowPos = 0; rowPos < rows.length; ++rowPos) { const rowId = rows[rowPos].rowId; - let tr_found = false; - for (let j = rowPos; j < trs.length; ++j) { - if (trs[j].rowId === rowId) { - tr_found = true; - if (rowPos === j) - break; - trs[j].inject(trs[rowPos], "before"); - const tmpTr = trs[j]; - trs.splice(j, 1); - trs.splice(rowPos, 0, tmpTr); - break; - } + const existingTr = trMap.get(rowId); + if (existingTr !== undefined) { + this.updateRow(existingTr, fullUpdate); } - if (tr_found) { // row already exists in the table - this.updateRow(trs[rowPos], fullUpdate); - } - else { // else create a new row in the table + else { const tr = this.createRowElement(rows[rowPos]); - // Insert - if (rowPos >= trs.length) { - tr.inject(this.tableBody); - trs.push(tr); - } - else { - tr.inject(trs[rowPos], "before"); - trs.splice(rowPos, 0, tr); - } + // TODO look into using DocumentFragment or appending all trs at once for add'l performance gains + // add to end of table - we'll move into the proper order later + this.tableBody.appendChild(tr); + trMap.set(rowId, tr); this.updateRow(tr, true); } } + // reorder table rows + let prevTr = null; + for (let rowPos = 0; rowPos < rows.length; ++rowPos) { + const { rowId } = rows[rowPos]; + const tr = trMap.get(rowId); + + const isInCorrectLocation = rowId === trs[rowPos]?.rowId; + if (!isInCorrectLocation) { + // move row into correct location + if (prevTr === null) { + // insert as first row in table + if (trs.length === 0) + this.tableBody.append(tr); + else + trs[0].before(tr); + } + else { + prevTr.after(tr); + } + } + prevTr = tr; + } + const rowPos = rows.length; while ((rowPos < trs.length) && (trs.length > 0))