Harshit Laddha
Harshit Laddha

Reputation: 2124

javascript convert nested dictionary to tree like array structure

I have a sample input of strings in the form of - "a.b.c", "a.d.e", etc.

I want to convert the string parts separated by "." to be represented in a tree structure.

So, I wrote the below function to create a nested dictionary object from such an array of input -

function items_to_tree(items) {
    var arr = {};
    items.forEach(function(item){
        var parts = item.split(".");
        var last = parts.pop();
        var cursor = arr;
        parts.forEach(function(part){
            if(!cursor[part]) cursor[part] = {};
            cursor = cursor[part];
        });
        cursor[last] = {};
    });
    return arr;
}

So, for example If I give the below sample input to this function -

var items = ["a.b", "a.c", "b.c", "a.c", "a.c.d", "a.b.d"]

I get {"a":{"b":{"d":{}},"c":{"d":{}}},"b":{"c":{}}} as expected.

But, I want the output to be in a format similar to this -

[{name: "a", children: [{name: "b", children: [{name: "d", children: []}]}]}, {name: "c", children: [{name: "d", children: []}]}, {name: "b", children: [{name: "c", children: []}]}]

Is there any way the items_to_tree function can be modified to return such output, or can the intermediate output from the items_to_tree [the nested dictionary] be transformed into this tree like javascript object array.

Upvotes: 1

Views: 1436

Answers (2)

Nina Scholz
Nina Scholz

Reputation: 386570

You could seach in the nested array for a given name and use that object or create a new one.

var items = ["a.b", "a.c", "b.c", "a.c", "a.c.d", "a.b.d"],
    result = [];

items.forEach(function (path) {
    path.split('.').reduce(function (level, key) {
        var temp = level.find(({ name }) => key === name);
        if (!temp) {
            temp = { name: key, children: [] };
            level.push(temp);
        }
        return temp.children;
    }, result);
});

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

Upvotes: 2

user3297291
user3297291

Reputation: 23372

I would write a second function for the final data conversion. Your items_to_tree is pretty generic and reusable. Converting this tree to the desired format can be done in a few lines:

function tree_to_format(tree) {
  return Object
    .keys(tree)
    .map(k => ({ name: k, children: tree_to_format(tree[k]) }))
};

Now, you can compose the function you need by piping the result of items_to_tree to tree_to_format:

function items_to_tree(items) {
    var arr = {};
    items.forEach(function(item){
        var parts = item.split(".");
        var last = parts.pop();
        var cursor = arr;
        parts.forEach(function(part){
            if(!cursor[part]) cursor[part] = {};
            cursor = cursor[part];
        });
        cursor[last] = {};
    });
    return arr;
}

function tree_to_format(tree) {
  return Object
    .keys(tree)
    .map(k => ({ name: k, children: tree_to_format(tree[k]) }))
};

console.log(
  tree_to_format(
    items_to_tree(["a.b", "a.c", "b.c", "a.c", "a.c.d", "a.b.d"])
  )
);

// or even:
const compose = (f, g) => x => f(g(x));
const items_to_format = compose(tree_to_format, items_to_tree);

Upvotes: 1

Related Questions