树构建无限递归检测

时间:2014-06-26 09:38:11

标签: javascript jquery algorithm recursion tree

我正在构建构建树的JQuery机制,它必须尽可能快。数据量非常大,因此主记录列用于“抓取”数据。一个中的所有相关节点都从webSQL db中选择 整个机制,但一部分完成,当组装树时,它必须检查没有创建无限递归。如果记录A是记录B的主记录和父记录,并且记录B是记录A的主记录和父记录,那么当前正在构建如A.Children[0] = BB.Children[0] = A的结构的机制。这一切都可以正常工作,但它确实被敲掉了.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()函数更快的内容?

整个树构建算法如下(不是我认为它是相关的,而是避免评论'显示代码')

$(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;
}

1 个答案:

答案 0 :(得分:0)

这取决于你的情况。如果你能够在添加/创建树时运行代码,你可以简单地创建一个节点id数组并进行检查,如

if(allNodes[searchKey])
   //etc

这当然是一个非常具体的解决方案,但从技术上讲,它的速度应尽可能快。