简体   繁体   中英

Array with children - display it in the form of parent-child hierarchy

I have the data as shown in step#1 and I would like to rearrange it in the form of parent with corresponding children as in step#2 based on the "id":

step#1:                                                                                             
[
    {"id": "card:1.usa", "name": "usa"}, {"id": "card:2", "name": "card2"}, {"id": "card:1", "name": "card1"}, {"id": "card:2.washington", "name": "washington"},
    {"id": "card:1.usa.illinios", "name": "illinios"}, {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]

step#2 :   
[
    {"id": "card:1", "name": "card1", "children": [ {"id": "card:1.usa", "name": "usa", "children":[ {"id": "card:1.usa.illinios", "name": "illinios", "children":[ {"id": "card:1.usa.illinios.city1", "name": "chicago"}] }] }    },
    {"id": "card:2", "name": "card2", "children": [ {"id": "card:2.washington", "name": "washington" }] }
]

I tried to do the following from my side, but this gets only first level children:

var cardData = [
    {"id": "card:1.usa", "name": "usa"}, {"id": "card:2", "name": "card2"}, {"id": "card:1", "name": "card1"}, {"id": "card:2.washington", "name": "washington"},
    {"id": "card:1.usa.illinios", "name": "illinios"}, {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]
var subCardList = [];
$scope.parentCard = [];
for(var i=0; i<cardData.length; i++){
    if( cardData[i].id.indexOf('.') > -1){
        subCardList.push( cardData[i] );
    }
}
for(var i=0; i<cardData.length; i++){
    for(var j=0; j<subCardList.length; j++){
        var cardObj = {};
        if( cardData[i].id == subCardList[j].id.substr(0, subCardList[j].id.indexOf('.')) ){ //found matching parent card
            cardObj.id = cardData[i].id;
            cardObj.children = subCardList[j];
            $scope.parentCard.push( cardObj );
        }
    }
}

Please let me know how I can achieve this through javascript/jquery?

I have improved my solution to make it more comprehensive and also handle the situation where all cards in the hierarchy are not present and in different order.

A sorting is performed on the sub-card array based on id before processing. This ensures order of cards do not break this code.

All top level cards are collected first. Each sub-card is then placed into buckets(Children collection).

I have provided additional data set (cardData1) to highlight boundary conditions.

Please let me know if you need more explanation.

 var cardData = [{ "id": "card:1.usa", "name": "usa" }, { "id": "card:2", "name": "card2" }, { "id": "card:1", "name": "card1" }, { "id": "card:2.washington", "name": "washington" }, { "id": "card:1.usa.illinios", "name": "illinios" }, { "id": "card:1.usa.illinios.city1", "name": "chicago" }] var cardData1 = [ { "id": "card:1.usa.illinios.city1.municipality1", "name": "DumDum" },{ "id": "card:1.usa", "name": "usa" }, { "id": "card:2", "name": "card2" }, { "id": "card:1", "name": "card1" }, { "id": "card:2.washington", "name": "washington" }, { "id": "card:1.usa.illinios.city1", "name": "chicago" }, ] var subCardList = []; var subCardMap = {}; var parentCardList = []; var cardMap = {}; for (var i = 0; i < cardData.length; i++) { if (cardData[i].id.indexOf('.') > -1) { subCardList.push(cardData[i]); subCardMap[cardData[i].id] = cardData[i]; } else { //top level cards var cardId = cardData[i].id; var parentCard = { id: cardId, name: cardData[i].name, children: [] }; cardMap[cardId] = parentCard; parentCardList.push(parentCard); } } //Sort the subcard list to ensure missing hierarchial cards do not break implementation subCardList.sort(function (a, b) { return a.id.toLowerCase().localeCompare(b.id.toLowerCase()); }); //Build buckets(children array) for child cards on the fly for (var j = 0; j < subCardList.length; j++) { var topCardId = subCardList[j].id.substr(0, subCardList[j].id.indexOf('.')); placeInBucket(topCardId, subCardList[j]); //find matching parent card from map } function placeInBucket(topCardId, childCard) { var topCard = cardMap[topCardId]; //get top card var childIds = childCard.id.split("."); childIds.splice(0, 1); //Remove the top card id var childId = ""; var bucket = topCard.children; //Assign the initial bucket as the topcard children array //loop through all the hierarchy and create complete hierarchy for (var i = 0; i < childIds.length; i++) { var key = topCardId + childId + "." + childIds[i]; if (!subCardMap[key]) { childId += "." + childIds[i]; continue; } //Do not build hierarchy for missing subcards in the id chain var child = cardMap[key]; if (!child) { bucket.push(childCard); cardMap[key] = childCard; //Register new child to cardMap break; } if (!child.children) child.children = []; //Add Children array to a leaf card if not existing bucket = child.children; childId += "." + childIds[i]; //Append the id to drill down the hierarchy } } console.log(JSON.stringify(parentCardList)); 

The short answer: you need a recursive function , a function that calls itself to nest the children until you find an element that has no children.

So, first, you find all the parents in your list:

[
    {"id": "card:2", "name": "card2"}, 
    {"id": "card:1", "name": "card1"}, 
]

Then, for each of these objects, you run through the array again, finding descendants. For example, for card:1 :

[
    {"id": "card:1.usa", "name": "usa"},
    {"id": "card:1.usa.illinios", "name": "illinios"},
    {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]

This is where we'll need recursivity: you need to repeat the same process again, taking this array as your input. So the "parents" you will find are:

[
    {"id": "card:1.usa", "name": "usa"}
]

And the children:

[
    {"id": "card:1.usa.illinios", "name": "illinios"},
    {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]

Since you still have children, you would repeat yet again, until you don't find any more children.

This example might not be as clean as it could be (I wasn't sure how to deal with the ids), but it works:

function nestChildren(list, startIndex){
    var parents = [];

    // Search list for "parents" --> objects with only one period-separated section
    for(var i=0;i<list.length;i++){
        var ids = list[i].id.substring(startIndex).split('.'); // Count only sections from parent id

        if (ids.length == 1){
            parents.push(list[i]);
        } 
    }

    for(var i=0;i<parents.length;i++){
        var parent = parents[i];

        var children = [];

        // Search list for "children" --> objects with ids that start with the parent id
        for(var j=0;j<list.length;j++){
            if (list[j].id.indexOf(parent.id) == 0 && list[j].id != parent.id){
                children.push(list[j]);
            }
        }

        if (children.length){
            // If there's any children, nest those children too
            // Pass the found children as the "list" parameter
            // and the parent's id length as the second (to count periods from that index)
            parent.children = nestChildren(children, parent.id.length + 1);
        }
    }

    return parents;
}

Working JSFiddle here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM