【发布时间】:2014-08-17 02:31:45
【问题描述】:
我正在研究构建树的JQuery 机制,它必须尽可能快。数据量非常大,因此使用主记录列能够从 webSQL db 中一次选择“抓取”所有相关节点。
整个机制,但完成了一部分,组装树时必须检查是否没有创建无限递归。如果记录 A 是记录 B 的主控和父级,记录 B 是记录 A 的主控和父级,那么目前正在构建像 A.Children[0] = B 和 B.Children[0] = A 这样的结构。这一切都可以正常工作,但它确实是 knockout.js 绑定并显示为可扩展列表给用户,这会导致在尝试显示树时溢出。
要求是检测此类循环并且不创建树关系。
检查项目是否已经在我提出的树中的机制是:
function InTree(master, item) {
return $.inArray(item, $.map(master, function recurs(n) {
return ($.isArray(n) ? $.map(n, recurs) : n);
})) != -1;
}
if(!InTree(tree, record))
{
//attach
} else {
// get next record
}
如果项目在树中,有什么比 InTree() 更快的函数可以得到我吗?
整个树的构建算法在下面(不是我认为它是相关的,而是为了避免 cmets '显示代码')
$(document).on('OnPreQuery', onTheMove.PageDataRoles, function (e, options) {
var isChildAttachmentQueued = true;
var knockoutContextName = options.knockoutContextName;
if (TreeEnabled(knockoutContextName)) {
var isModelReadyToAttachChildren = function () {
var isReady = false;
if (PageObj[knockoutContextName] != undefined) {
isReady = (PageObj[knockoutContextName]().length > 0) && isChildAttachmentQueued;
}
return isReady;
};
var businessComponent = eval(knockoutContextName);
var treeSettings = businessComponent.Tree;
treeSettings.knockoutContextName = knockoutContextName;
$(businessComponent).on('OnPreUIUpdate', function (e, options) {
if (isModelReadyToAttachChildren()) {
getChildrenForMasterRecordList({
parentTable: businessComponent.primaryTableName,
knockoutContextName: treeSettings.knockoutContextName,
parentIdColumn: treeSettings.ParentIdColumn,
masterIdColumn: treeSettings.MasterIdColumn
});
isChildAttachmentQueued = false;
}
});
}
});
function TreeEnabled(knockoutContextName) {
var isTreeEnabled = false;
try {
eval(knockoutContextName);
} catch (e) {
return isTreeEnabled;
}
var treeSettings = eval(knockoutContextName).Tree;
if (treeSettings && treeSettings.IncludeChildren) {
isTreeEnabled = true;
}
return isTreeEnabled;
}
function ComposeRecordsToTreeStructure(results, tableArray, columnArray, options) {
if (results.rows.length > 0) {
if (options.parentLayerIdList == undefined) {
options.parentLayerIdList = options.masterIdList;
}
if (options.orphans == undefined) {
options.orphans = [];
}
var knockoutContextName = options.knockoutContextName;
var childRecordIdArray = [];
if (options.runningOnOrphans) {
if (options.orphans.length > 0) {
for (var j = 0; j < options.orphans.length; j++) {
var rowRecord = options.orphans[j];
var rowRecordParentId = rowRecord[options.parentIdColumn];
var result = EstablishParentChildConnectionOnAlreadyProcessedNodes(rowRecord, rowRecordParentId, options.parentLayerIdList, knockoutContextName, childRecordIdArray, options);
if (result.hasEstablishedConnection) {
childRecordIdArray = result.childRecordIdArray;
}
}
options.orphans = $.grep(options.orphans, function (item) {
return $.inArray(item['Id'], childRecordIdArray) == -1;
});
}
} else {
for (var i = 0; i < results.rows.length; i++) {
var rowRecord = results.rows.item(i);
var rowRecordParentId = rowRecord[options.parentIdColumn];
if (rowRecord[options.parentIdColumn] == '' || rowRecord[options.masterIdColumn] == '' || rowRecord[options.masterIdColumn] == rowRecord['Id']) {
rowRecord.isInvalid = true;
} else if ($.inArray(rowRecord['Id'], options.masterIdList) != -1) {
masterRecordClone = $.grep(PageObj[knockoutContextName](), function (item) { return item.Fields.Id() == rowRecord['Id'] })[0];
if (masterRecordClone != undefined && masterRecordClone.Children) {
rowRecord.Children = masterRecordClone.Children;
}
}
if (rowRecord.isInvalid == true) {
if (rowRecord[options.masterIdColumn] != rowRecord['Id']) {
var result = EstablishParentChildConnection(rowRecord, rowRecord[options.masterIdColumn], options.parentLayerIdList, knockoutContextName, childRecordIdArray, options);
if (result.hasEstablishedConnection) {
childRecordIdArray = result.childRecordIdArray;
EstablishParentChildConnectionOnAlreadyProcessedNodes(rowRecord, rowRecordParentId, options.parentLayerIdList, knockoutContextName, childRecordIdArray, options);
}
}
} else {
var result = EstablishParentChildConnectionOnAlreadyProcessedNodes(rowRecord, rowRecordParentId, options.parentLayerIdList, knockoutContextName, childRecordIdArray, options);
if (result.hasEstablishedConnection) {
childRecordIdArray = result.childRecordIdArray;
} else {
var recordObject = AddIsExpandedProperty(rowRecord);
options.orphans.push(recordObject);
options.runningOnOrphans = true;
}
}
}
}
if (options.orphans.length > 0 && childRecordIdArray.length > 0) {
options.parentLayerIdList = childRecordIdArray;
ComposeRecordsToTreeStructure(results, tableArray, columnArray, options);
}
}
onTheMove.seleniumHelper.markPageAsLoaded();
}
function EstablishParentChildConnectionOnAlreadyProcessedNodes(rowRecord, rowRecordParentId, parentLayerIdList, knockoutContextName, childRecordIdArray, options) {
var result = EstablishParentChildConnection(rowRecord, rowRecordParentId, parentLayerIdList, knockoutContextName, childRecordIdArray);
if (result.hasEstablishedConnection) {
childRecordIdArray = result.childRecordIdArray;
} else {
var result = EstablishParentChildConnection(rowRecord, rowRecordParentId, childRecordIdArray, knockoutContextName, childRecordIdArray);
if (result.hasEstablishedConnection) {
childRecordIdArray = result.childRecordIdArray;
} else {
var matchingOrphans = $.grep(options.orphans, function (item) {
return item['Id'] == rowRecordParentId;
});
if (matchingOrphans.length > 0) {
AttachPassedChildRecord(rowRecord, matchingOrphans);
var result = {
hasEstablishedConnection: true
};
}
}
}
return {
childRecordIdArray: childRecordIdArray,
hasEstablishedConnection: result.hasEstablishedConnection
};
}
function EstablishParentChildConnection(rowRecord, rowRecordParentId, parentLayerIdList, knockoutContextName, childRecordIdArray) {
var hasEstablishedConnection = false;
var parentPosition = $.inArray(rowRecordParentId, parentLayerIdList);
if (parentPosition != -1) {
AttachChildRecordsToParents(rowRecord, parentLayerIdList[parentPosition], knockoutContextName);
childRecordIdArray = AddChildRecordsToNextParentList(rowRecord, childRecordIdArray);
childRecordIdArray.push(rowRecord['Id']);
hasEstablishedConnection = true;
}
return {
childRecordIdArray: childRecordIdArray,
hasEstablishedConnection: hasEstablishedConnection
};
}
function AddChildRecordsToNextParentList(childRecord, childRecordIdArray) {
if (childRecord.Children != undefined) {
for (var i = 0; i < childRecord.Children.length; i++) {
childRecordIdArray.push(childRecord.Children[i]['Id']);
if (childRecord.Children[i].Children != undefined) {
AddChildRecordsToNextParentList(childRecord.Children[i], childRecordIdArray);
}
}
}
return childRecordIdArray;
}
function RowsToListDataStructure(results) {
var array = [];
for (var i = 0; i < results.rows.length; i++) {
array.push(results.rows.item(i));
}
return array;
}
function AttachChildRecordsToParents(recordRow, id, knockoutContextName) {
var childTreeOptions = {
id: id,
knockoutContextName: knockoutContextName,
results: []
};
findObjectsInChildTreeById(childTreeOptions);
if (childTreeOptions.results.length > 0) {
AttachPassedChildRecord(recordRow, childTreeOptions.results);
}
}
function AttachPassedChildRecord(recordObject, pageObjParentResults) {
for (var i = 0; i < pageObjParentResults.length; i++) {
if (pageObjParentResults[i].Children == undefined) {
pageObjParentResults[i].Children = [];
}
if ($.grep(pageObjParentResults[i].Children, function (children) {
return children['Id'] == recordObject['Id'];
}).length == 0) {
recordObject = AddIsExpandedProperty(recordObject);
pageObjParentResults[i].Children.push(recordObject);
}
}
}
function AddIsExpandedProperty(recordObject) {
recordObject.IsExpanded = ko.observable(false);
return recordObject;
}
function findObjectsInChildTreeById(options) {
if (options.item == undefined) {
if (typeof PageObj[options.knockoutContextName] != 'undefined') {
for (var item in PageObj[options.knockoutContextName]()) {
findObjectsInChildTreeById({
item: PageObj[options.knockoutContextName]()[item],
id: options.id,
results: options.results
});
}
}
} else {
if (typeof options.item.Fields != 'undefined') {
if (options.item.Fields['Id']() == options.id)
options.results.push(options.item);
} else {
if (options.item['Id'] == options.id)
options.results.push(options.item);
}
if (options.item.Children != undefined) {
for (var item in options.item.Children) {
findObjectsInChildTreeById({
item: options.item.Children[item],
id: options.id,
results: options.results
});
}
}
}
}
function getChildrenForMasterRecordList(options) {
var parentTable = options.parentTable,
masterIdColumn = options.masterIdColumn,
parentIdColumn = options.parentIdColumn,
knockoutContextName = options.knockoutContextName,
masterIds = getParentIdsAndMastersOfParentsFromPageObj(knockoutContextName, masterIdColumn);
for (var item in PageObj[options.knockoutContextName]()) {
AddIsExpandedProperty(PageObj[knockoutContextName]()[item]);
}
var dbManager = new OnTheMoveDatabaseManager();
dbManager.queryDatabase({
statement: {
Tables: [{
Alias: parentTable,
JoinSpec: null,
JoinType: "",
Name: parentTable
}, {
Alias: "Record",
JoinSpec: "Record.Id = " + parentTable + ".Id",
JoinType: "INNER",
Name: "Record"
}],
WhereClause: parentTable + "." + masterIdColumn + " IN ('" + masterIds.join("','") + "') AND Record.RecordType ='" + parentTable + "'",
SelectFields: [{
IsAggregate: false,
Name: "*"
}],
DisablePaging: true,
OrderClause: "Record.Id"
},
knockoutContextName: knockoutContextName,
isObservable: false,
masterIdColumn: masterIdColumn,
masterIdList: masterIds,
parentIdColumn: parentIdColumn,
parentTable: options.parentTable,
success: function (results, tableArray, columnArray, options) {
ComposeRecordsToTreeStructure(results, tableArray, columnArray, options);
}
});
}
function getParentIdsAndMastersOfParentsFromPageObj(knockoutContextName, masterColumnName) {
var list = [];
if (typeof PageObj[knockoutContextName] != 'undefined') {
for (var item in PageObj[knockoutContextName]()) {
if ($.inArray(PageObj[knockoutContextName]()[item].Fields['Id'](), list) == -1) {
list.push(PageObj[knockoutContextName]()[item].Fields['Id']());
}
if (PageObj[knockoutContextName]()[item].Fields[masterColumnName]() != '' && $.inArray(PageObj[knockoutContextName]()[item].Fields[masterColumnName](), list) == -1) {
list.push(PageObj[knockoutContextName]()[item].Fields[masterColumnName]());
}
}
}
return list
}
function InTree(master, item) {
return $.inArray(item, $.map(master, function recurs(n) {
return ($.isArray(n) ? $.map(n, recurs) : n);
})) != -1;
}
【问题讨论】:
标签: javascript jquery algorithm recursion tree