5

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?

3 Answers 3

5

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);
Sign up to request clarification or add additional context in comments.

3 Comments

A small annotation on a = obj[name] || { name: name }; here you can use peep instead of the new object and replace a with obj[name], complete obj[name] = obj[name] || peep; where a use just obj[name] now you can delete the last line obj[name] = obj[name] || a;.
@NinaScholz Not sure about that. Have you tried it? For a start, the output format doesn't include parent property. Also, there's method behind the apparent madness of the code to allow the input data to be in arbitrary order
it works, pl see my answer. you are right regarding the property parent, i include just all properties. (op does not ask them).
4

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

5 Comments

I had similar code, using reduce, and a "special" property on the reduction var, even called it _ - I thought it just may be a little overwhelming for the OP - however I still used my logic to return the exact structure requested :p
Sorry for necroposting, but..Your both codes not working if there are some Root comments and not only one, where parent is Null.
@DenimTornado, please see part 2 for more than one root element.
@NinaScholz cool, it works, just one moment, can you clarify that ".null.children;" part at the end?
r[a.parent] creates a property with 'null' (null as string as all keys are strings) as key, which is the known symbol for a root object. this is is taken to get the children of it.
2

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);

4 Comments

This will fail if the provided array is not in the correct order. var data = [, { name: "B", parent: "A" }, { name: "A", parent: null }, { name: "C", parent: "A" }, { name: "D", parent: "B" }, { name: "E", parent: "B" } ]; will break for example.
@SimonBrahan no it won't fail. You forgot to remove comma in your data.
@madox2, It should be console.log(tree), right? Could you explain how this works? You are not modifying the tree object anywhere but the result is in tree. I am confused.
@espeirasbora you are right, that was kind of misstyping. I approved edit to fix it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.