Get the level of a hierarchy

偶尔善良 提交于 2019-11-29 03:50:57

问题


I have an array of objects, Where each object has an id and a ParentId property (so they can be arranged in trees). They are in no particular order.

Please note that the id's and parentId's will not be integers, they will be strings (just wanted to have the sample code cleaner..)

There is only one root: lets say its id:1 The data looks like so:

 data = [
   {
        id:"id-2",
        parentId:"id-3"
    },
    {
        id:"id-4",
        parentId:"2"
    },
    {
        id:"id-3",
        parentId:"id-4"
    },
    {
        id:"id-5",
        parentId:"id-4"
    },
    {
        id:"id-6",
        parentId:"id-1"
    },
    {
        id:"id-7",
        parentId:"id-1"
    }
    // and so on...
]

I am looking for a efficient way to give each object a level property which should specify the nested level it is...

They should then look like this:

data = [
    {
        id:"id-2",
        parentId:"id-1",
        level:2
    },
    {
        id:"id-3",
        parentId:"id-4",
        level:5

    },
    {
        id:"id-4",
        parentId:"id-2",
        level:3

    },
    {
        id:"id-5",
        parentId:"id-4",
        level:5
    },
    {
        id:"id-6",
        parentId:"id-1",
        level:2

    },
    {
        id:"id-7",
        parentId:"id-3",
        level:4
    }
    // and so on...
]

In short:

I want that level to be added dynamically via looping thru the array and figuring out the hierarchy..

Additionally, (if posible) they should then be sorted according to there order, like for instance all objects level:3's from the same parent should be next to each other, not that there should be siblings of the same parent next to each other rather then two cousins of level 3 next to each other.


回答1:


A working example of the below code is on jsFiddle.

Index the tree by id and traverse it upwards, from each node, and count until you hit the root. By indexing first, we approach O(n) time complexity (depending on tree density). ****Updated to satisfy the sorting requirement, and allow exclusion of root node***:

function levelAndSort(data, startingLevel) {
    // indexes
    var indexed = {};        // the original values
    var nodeIndex = {};      // tree nodes
    var i;
    for (i = 0; i < data.length; i++) {
        var id = data[i].id;
        var node = {
            id: id,
            level: startingLevel,
            children: [],
            sorted: false
        };
        indexed[id] = data[i];
        nodeIndex[id] = node;
    }

    // populate tree
    for (i = 0; i < data.length; i++) {
        var node = nodeIndex[data[i].id];
        var pNode = node;
        var j;
        var nextId = indexed[pNode.id].parentId;
        for (j = 0; nextId in nodeIndex; j++) {
            pNode = nodeIndex[nextId];
            if (j == 0) {
                pNode.children.push(node.id);
            }
            node.level++;
            nextId = indexed[pNode.id].parentId;
        }
    }

    // extract nodes and sort-by-level
    var nodes = [];
    for (var key in nodeIndex) {
        nodes.push(nodeIndex[key]);
    }
    nodes.sort(function(a, b) {
        return a.level - b.level;
    });

    // refine the sort: group-by-siblings
    var retval = [];
    for (i = 0; i < nodes.length; i++) {
        var node = nodes[i];
        var parentId = indexed[node.id].parentId;
        if (parentId in indexed) {
            var pNode = nodeIndex[parentId];
            var j;
            for (j = 0; j < pNode.children.length; j++) {
                var child = nodeIndex[pNode.children[j]];
                if (!child.sorted) {
                    indexed[child.id].level = child.level;
                    retval.push(indexed[child.id]);
                    child.sorted = true;
                }
            }
        }
        else if (!node.sorted) {
            indexed[node.id].level = node.level;
            retval.push(indexed[node.id]);
            node.sorted = true;
        }
    }
    return retval;
}

Example:

// level 0 (root) excluded
var startingLevel = 1;
var someData = [
    {id : "id-1", parentId : "id-0"},
    {id : "id-2", parentId : "id-0"},
    {id : "id-3", parentId : "id-2"},
    {id : "id-4", parentId : "id-3"},
    {id : "id-5", parentId : "id-4"},
    {id : "id-6", parentId : "id-4"},
    {id : "id-7", parentId : "id-0"},
    {id : "id-8", parentId : "id-1"},
    {id : "id-9", parentId : "id-7"},
    {id : "id-10", parentId : "id-1"},
    {id : "id-11", parentId : "id-1"},
    {id : "id-12", parentId : "id-1"}
];
var outputArray = levelAndSort(someData, startingLevel);

Output:

Note

If you change the input order, the sort comes out a little different, but it's still correct (i.e., in level-order, grouped by sibling).




回答2:


I'm not sure where you get the value for level so I'll assume that its just an integer. BTW, you can add the level property to each of your array's item by looping through it.

for (var i = 0, l = data.length; i < l; i++) { 
    data[i].level = i
}

which will give

data = [{id:"1", parentId:"3", level:0 }, {id:"2", parentId:"1", level:1 } ...]



回答3:


Here is your working code. Level starts at 2.

ALERT: If a level cannot be calculated, the application may go into an infinite loop. So, make sure the parentId is valid for all objects and at least one of them have parentId="id-1".

<script type="text/javascript">
    data = [
        {
            id:"id-2",
            parentId:"id-3"
        },
        {
            id:"id-4",
            parentId:"id-2"
        },
        {
            id:"id-3",
            parentId:"id-5"
        },
        {
            id:"id-5",
            parentId:"id-1"
        }
    ];

    function processData() {
        flag = true;

        while(flag) {
            flag = false;

            for(var i = 0; i < data.length; i++) {
                if(data[i].parentId == "id-1") {
                    data[i].level = 2;
                } else {
                    l = getLevel(data[i].parentId);
                    if(l > 0) {
                        data[i].level = l + 1;
                    } else {
                        flag = true;
                    }
                }
            }
        }
    }

    function getLevel(id) {
        for(var i = 0; i < data.length; i++) {
            if(data[i].id == id) {
                if(data[i].level) {
                    return data[i].level;
                } else {
                    return 0;
                }
            }
        }

        return 0;
    }

    processData();

    console.log(data);

</script>



回答4:


One way to address this without the need for recursion is to create a DOM hierarchy. For each item in the array, create a div and attach it to its parent. then walk the DOM and assign the levels (top is level 1, then add 1 for each child).

I have set up a rough demo here: http://jsfiddle.net/4AqgM/

An excerpt from the code:

top.dataset.level="1";
var topChildren=top.getElementsByTagName("div");
for (var i=0;i<topChildren.length;i++) {
topChildren[i].dataset.level=parseInt(topChildren[i].parentNode.dataset.level)+1;
}


来源:https://stackoverflow.com/questions/14132831/get-the-level-of-a-hierarchy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!