我用js + jQuery编写了这个函数。
这是一个链接到jsFiddle,显示我的问题:http://jsfiddle.net/anarnold/LpBaW/
此函数的目标是扫描表并检查具有某些字段(td)值匹配的行。然后为行分配一个表示它们是否唯一的类,并将匹配行的数量打印到每行的最后一个字段(td)中。
它基本上使用了这种结构的嵌套循环:
对于每一行......扫描整个表格以查找匹配项。
我识别行的方法是将每行的字段(td)文本连接到每行的最终字段(td)上的rowid属性。
当前函数工作正常,但是大表〜2000行变得非常慢。
必须有一种更有效和更优雅的方法来实现这一目标。任何帮助将不胜感激!
下面是一个使用关联数组存储结果然后迭代它的示例:
var rowIdCnt = {};
function unqOrMsgTest() {
// Store counts here
var rowIdCnt = {};
// loop through check tds
$("#binning tr td[col=check]").each(function() {
// grab row identifer to check against other rows
var rowId = $(this).attr("rowid");
if (rowId in rowIdCnt) {
rowIdCnt[rowId] ++;
} else {
rowIdCnt[rowId] = 1;
}
});
// Now iterate over each count and update the table appropriately:
$.each(rowIdCnt, function(rowId, cnt) {
//this bit of logic picks a class to assign rows
var resultClass = "notUnique";
if (cnt < 2) {
resultClass = "unique";
}
//apply the row class and print the redundancy number into td
$('#binning tr td[rowid='+rowId+']').text(cnt).parent().addClass(resultClass);
});
}
这是一种更好的预成型方法。我已经删除了尽可能多的冗余DOM调用以及更正了无效属性(HTML标记只能支持某些属性..自定义属性需要以data-
为前缀)
$(document).ready(function(){ //this is just to fire the function
$("#unqOrMsgTestFire").click(function(){
unqOrMsgTest();
});
});
function check_unique(row, collection) {
var unique = true, rowid = $(row).children('td[data-col=check]')[0].getAttribute('data-rowid');
collection.each(function() {
if( $(this).children('td[data-col=check]')[0].getAttribute('data-rowid') == rowid ) {
unique = false;
}
});
return unique;
}
function unqOrMsgTest() {
var collection = $("#binning tbody").children('tr');
collection.each(function(i, el){
el.className += check_unique( el, collection.not(el) ) ? ' unique' : 'notUnique';
});
}
为每个表条目创建哈希值并使用哈希表或在检查重复项之前对它们进行排序,因此您只需要比较邻居。
更优雅的方法是在构建此表之前在数据级别执行此操作。
这是我推荐的解决方案:
function unqOrMsgTest() {
var rows = $("#binning tbody").children('tr');
var totalRows = rows.length;
var idLookup = {};
var i, rowId, resultClass, checkColumn, rowCount, row;
// loops through all rows, convert to jQuery objects and track the IDs
for (i = 0; i < totalRows; i++)
{
row = $(rows[i]);
rowId = row.children('td[col="check"]').attr("rowid");
rows[i] = row;
idLookup[rowId] = (rowId in idLookup) ? idLookup[rowId] + 1 : 1;
}
// loop through each row and check them for redundancy
for (var i = 0; i < totalRows; i++ )
{
// grab row identifer to check against the id lookup
row = rows[i];
checkColumn = row.children('td[col="check"]');
rowId = checkColumn.attr("rowid");
//this bit of logic picks a class to assign rows
rowCount = idLookup[rowId];
resultClass = rowCount < 2 ? "unique" : "notUnique";
//apply the row class and print the redundancy number into td
checkColumn.text(rowCount);
row.attr("class", resultClass);
};
}
类似于上面的答案,建议使用关联数组(或散列)来存储id和计数,我还删除了对list.each( function() {...} )
的所有调用,并最小化了从dom元素到jQuery对象的转换次数。
我删除each
的用法的原因是因为每次迭代都会创建一个新的匿名函数,也会调用从此到$(this)的冗余转换,更不用说堆栈抖动了。我只想说一个简单的for循环就是所需要的,而且速度要快得多。
有关jQuery陷阱的更多信息,请查看jQuery pitfalls to avoid
$(function(){ //this is just to fire the function
$("#unqOrMsgTestFire").click(unqOrMsgTest);
$("#spawn").click(function(){
var blueprint = $("#binning tbody tr").first();
for(var i = 0; i < 1000; i++)
blueprint.clone().appendTo("#binning tbody").find('td[rowid]').attr('rowid', Math.floor(Math.random()*500));
});
});
function unqOrMsgTest() {
console.profile();
var toCheck = $("#binning > tbody > tr > td[col=check]"),
ignore = {},
curId,
currentlyProcessing,
rowsAmount,
i;
i = toCheck.length - 1;
while( i >= 0 ) {
curId = toCheck.eq(i).attr("rowid");
if( !(curId in ignore) ) {
ignore[curId] = undefined;
currentlyProcessing = $("#binning > tbody > tr > td[rowid=" + curId + "]");
rowsAmount = currentlyProcessing.length;
currentlyProcessing
.text( rowsAmount )
.parent().attr('class', rowsAmount > 1 ? "notUnique" : "unique");
}
i--;
}
console.profileEnd();
}
rowsAmount = currentlyProcessing.length;
currentlyProcessing
.text( rowsAmount )
.parent().attr('class', rowsAmount > 1 ? "notUnique" : "unique");
toCheck = toCheck.not( currentlyProcessing );
}
}