简体   繁体   中英

create nested json from array of objects dynamically

I have array of objects like this

{ title: "A", parent_id: "root",has_children: true}

{ title: "A1", parent_id: "A",has_children: true}

{ title: "A11", parent_id: "A1",has_children: false}
{ title: "A12", parent_id: "A1",has_children: false}
{ title: "A13", parent_id: "A1",has_children: false}

{ title: "B", parent_id: "root",has_children: true}

{ title: "B1", parent_id: "A",has_children: true}

{ title: "B11", parent_id: "B1",has_children: false}
{ title: "B12", parent_id: "B1",has_children: false}
{ title: "B13", parent_id: "B1",has_children: false}

each record has params saying whether it has any children or not and what is the parent for that card.

record with "root" is the top level card.

From the data above I want something like below

[
  {
    "title": "A",
    "children": [
      {
        "title": "A1",
        "children": [
          {
            "title": "A11"
          },
          {
            "title": "A12"
          },
          {
            "title": "A13."
          }
        ]
      },
      {
        "title": "A2",
        "children": [
          {
            "title": "A21"
          },
          {
            "title": "A22"
          },
          {
            "title": "A23"
          }
        ]
      }
    ]
  },
  {
    "title": "B",
    "children": [
      {
        "title": "B1",
        "children": [
          {
            "title": "B11"
          },
          {
            "title": "B12"
          },
          {
            "title": "B13."
          }
        ]
      },
      {
        "title": "B2",
        "children": [
          {
            "title": "B21"
          },
          {
            "title": "B22"
          },
          {
            "title": "B23"
          }
        ]
      }
    ]
  }
]

I'm trying for a while to do this but couldn't do it, here is what I tried

I'm doing this in meteor using mongodb

getUserJSON  = function(userId){
  var userJSON = [];
  getJSONCards(userId,'root', userJSON)

}

getJSONCards = function(userId, parent_id, userData){
  var allCards = userCards.find({ $and: [ { user_id: userId }, { parent_id: parent_id } ] }).fetch();
  if(allCards.length > 0){
    allCards.forEach(function (cardInfo) {
      var isExist = $.grep(userData, function(e){ return e.content === parent_id; });
      if(isExist){
        //here I don't know how to insert nested cards
      }
    });
  }
}

But I'm hoping for simple js solution

The solution below uses the following approach:

  1. Index each item into an object by their respective title value from the allCards collection by using Array.prototype.reduce() .
  2. Use Array.prototype.filter() to retain the root items in the collection, while at the same time assign each item as a child to their respective parents if they do exist in the indexed variable.

 var allCards = [ { title: "A", parent_id: "root", has_children: true}, { title: "A1", parent_id: "A", has_children: true}, { title: "A11", parent_id: "A1", has_children: false}, { title: "A12", parent_id: "A1", has_children: false}, { title: "A13", parent_id: "A1", has_children: false}, { title: "B", parent_id: "root", has_children: true}, { title: "B1", parent_id: "A", has_children: true}, { title: "B11", parent_id: "B1", has_children: false}, { title: "B12", parent_id: "B1", has_children: false}, { title: "B13", parent_id: "B1", has_children: false} ]; // index each item by title var indexed = allCards.reduce(function(result, item) { result[item.title] = item; return result; }, {}); // retain the root items only var result = allCards.filter(function(item) { // get parent var parent = indexed[item.parent_id]; // make sure to remove unnecessary keys delete item.parent_id; delete item.has_children; // has parent? if(parent) { // add item as a child parent.children = (parent.children || []).concat(item); } // This part determines if the item is a root item or not return !parent; }); document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>'); 

Here is one way of doing it. Iterate the array of objects, building a new object for each one and store it in a cache so you can directly modify that object when its children are processed. Parents must appear before children for this to work, otherwise you'd have to build in multiple passes.

// define data array
var data = [
    { title: "A", parent_id: "root",has_children: true},

    { title: "A1", parent_id: "A",has_children: true},

    { title: "A11", parent_id: "A1",has_children: false},
    { title: "A12", parent_id: "A1",has_children: false},
    { title: "A13", parent_id: "A1",has_children: false},

    { title: "B", parent_id: "root",has_children: true},

    { title: "B1", parent_id: "A",has_children: true},

    { title: "B11", parent_id: "B1",has_children: false},
    { title: "B12", parent_id: "B1",has_children: false},
    { title: "B13", parent_id: "B1",has_children: false}
];

var root = {};
var parentCache = {};
// for each element definition in the data array
for (var i = 0; i < data.length; i++) {
    var element = data[i];
    var title = element.title;
    // XXX - check for duplicate title here.

    // create a new object and initialize
    var newObj = {"title" : title};
    if (element.has_children) {
        newObj["children"] = [];
    }
    // put this object into its parent
    if (element.parent_id === "root") {
        root[title] = newObj;
    } else {
        // XXX - if the parent isn't defined first this will fail
        var parent = parentCache[element.parent_id];
        parent.children.push(newObj);
    }
    // store this object in case it is a parent
    parentCache[title] = newObj;
}

console.log(JSON.stringify(root));

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