'Tree from an associative array

There is an array:

let docs = [      
    { "_id":"1", parent:"_", "title":"one"},
    { "_id":"2", parent:"1", "title":"two"},
    { "_id":"4", parent:"_", "title":"title"},
    { "_id":"5", parent:"4", "title":"www"},
    {"_id":"_", "name":"root" },
];

I need to get out of it that's a tree:

{'_id':'_','name':'root','child':
    [
        {'_id':'1','parent':'_','title':'one','child':
            [
                {'_id':'2','parent':'1','title':'two','child':[]}
            ]
        },
        {'_id':'4','parent':'_','title':'title','child':
            [
                {'_id':'6','parent':'4','title':'vvv','child':[]}
            ]
        }
    ]
}

But my code only works if the parent element is always higher on the list than the children, and I want to make that work universally.

This is code:

let node = {};
for (let doc of docs) {      
    doc.child = [];
    node[doc._id] = doc;

    if (typeof doc.parent === "undefined")
        tree = doc;
    else 
        node[doc.parent].child.push(doc);
}

console.log('tree->', JSON.stringify(tree)); 

code on codepen: http://codepen.io/alex183/pen/OWvrPG?editors=0112



Solution 1:[1]

This is a proposal with Array#reduce and Map. It sorts the array in advance.

var docs = [{ _id: "1", parent: "_", title: "one" }, { _id: "2", parent: "1", title: "two" }, { _id: "4", parent: "_", title: "title" }, { _id: "5", parent: "4", title: "www" }, { _id: "_", name: "root" }],
    order = { undefined: -2, _: -1 },
    tree = docs
        .sort((a, b) => (order[a.parent] || a.parent) - (order[b.parent] || b.parent) || a._id - b._id)
        .reduce(
            (m, a) => (
                m
                    .get(a.parent)
                    .push(Object.assign({}, a, { child: m.set(a._id, []).get(a._id) })),
                m
            ),
            new Map([[undefined, []]])
        )
        .get(undefined);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Solution 2:[2]

The quick and dirty way is to use a sort function.

docs = docs.sort((a, b) => (a._id - b._id));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nina Scholz
Solution 2 D. Walsh