简体   繁体   中英

Converting an array into a nested object in javascript

I have typical organization hierarchy . For example.

D,E is reporting to B. B,C is reporting to A.

A is the top most node. But I receive this data as a flat array with an attribute pointing to the parent.

    [{
      name: "A",
      parent: null
    },
    {
      name: "B",
      parent: "A"
    },
    {
      name: "C",
      parent: "A"
    },
    {
      name: "D",
      parent: "B"
    },
    {
      name: "E",
      parent: "B"
    }]

But I want to convert this in to single nested object or a tree . A root node has children attribute with the children embedded and each child has its own children attribute like this.

    {
      name: "A",
      children: [{
        name: "C"
        children: [{
          name: "D"
        },{
          name: "E"
        }]
      },{
        name: "C"
      }]
    }

How can I do this in javascript efficiently?

Unlike the other solutions, this uses a single loop - the order of data is unimportant - example is not in the same order as question

var peeps = [
    { name: "D", parent: "B" }, 
    { name: "B", parent: "A" },
    { name: "A", parent: null }, 
    { name: "C", parent: "A" }, 
    { name: "E", parent: "B" }
];

var tree;
var obj = {};
peeps.forEach(function (peep) {
    var name = peep.name,
        parent = peep.parent,
        a = obj[name] || { name: name };
    if (parent) {
        obj[parent] = obj[parent] || { name: parent };
        obj[parent].children = obj[parent].children || [];
        obj[parent].children.push(a);
    } else {
        tree = obj[name];
    }
    obj[name] = obj[name] || a;
});
console.log(tree);

This solution features Jaromanda X' solution with some additions.

  1. Array.prototype.reduce instead of Array.prototype.forEach , because of the need of a temporary variable and the return value.

  2. The content of r[a.name].children is preserved and assigned to a.children .

  3. Node a is assigned to r[a.name] . Therefore all properties of the node object remain, like prop1 ... prop5 .

  4. The root node is assigned to r._ for later use.

 var data = [ { name: "D", parent: "B", prop1: 'prop1' }, { name: "B", parent: "A", prop2: 'prop2' }, { name: "A", parent: null, prop3: 'prop3' }, { name: "C", parent: "A", prop4: 'prop4' }, { name: "E", parent: "B", prop5: 'prop5' } ], tree = data.reduce(function (r, a) { a.children = r[a.name] && r[a.name].children; r[a.name] = a; if (a.parent) { r[a.parent] = r[a.parent] || {}; r[a.parent].children = r[a.parent].children || []; r[a.parent].children.push(a); } else { r._ = a; } return r; }, {})._; document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');

For more than one roots, you could use the parent of root as accessor for the children and return this array

 var data = [{ name: "D", parent: "B", prop1: 'prop1' }, { name: "B", parent: "A", prop2: 'prop2' }, { name: "A", parent: null, prop3: 'prop3' }, { name: "C", parent: "A", prop4: 'prop4' }, { name: "E", parent: "B", prop5: 'prop5' }, { name: "A1", parent: null, prop3: 'prop3' }], tree = data.reduce(function (r, a) { if (r[a.name] && r[a.name].children) { // prevent empty children array a.children = r[a.name].children; } r[a.name] = a; r[a.parent] = r[a.parent] || {}; r[a.parent].children = r[a.parent].children || []; r[a.parent].children.push(a); return r; }, {}).null.children; // take root value as property accessor console.log(tree);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

You can do this using while loop:

var data = [
    { name: "A", parent: null },
    { name: "B", parent: "A" },
    { name: "C", parent: "A" },
    { name: "D", parent: "B" },
    { name: "E", parent: "B" }
];

var root = data.find(function(item) {
    return item.parent === null;
});

var tree = {
    name: root.name
};

var parents = [tree];
while (parents.length > 0) {
    var newParents = [];
    parents.forEach(function(parent) {
        var childs = data.filter(function(item) {
            return item.parent == parent.name
        }).forEach(function(child) {
            var c = { name: child.name };
            parent.children = parent.children || [];
            parent.children.push(c);
            newParents.push(c);
        });
    });
    parents = newParents;
}

console.log(tree);

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