Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

How to convert folder structure JSON String to JS Array. I've following JSON string

   [{ "Name": "A", "ParentName": "Config", "Type": "default" },
{ "Name": "SubA", "ParentName": "A", "Type": "default" },
{ "Name": "SubAFile", "ParentName": "SubA", "Type": "file" },
{ "Name": "B", "ParentName": "Config", "Type": "default" },
{ "Name": "C", "ParentName": "Config", "Type": "default" }]

I want to make JS Array object out of this in following format

   var NewStr = [{
       "name": 'A',
       "id": 'A',
       "icon": 'fa fa-folder',
       "items": [{
           "title": "A",
           "icon": "fa fa-folder",
           "id": "A",
           "items": [{
               "name": "subA",
               "icon": "fa fa-folder",
               "id": "subA",
               "items": [{
                   "title": "SubA",
                   "icon": "fa fa-folder",
                   "id": "SubA",
                   "items": [{
                       "name": "SubAFile",
                       "icon": "fa fa-file"
                   }]
               }]
           }]
       }]
   }, {
       "name": 'B',
       "id": 'b',
       "icon": "fa fa-folder"
   }, {
       "name": 'C',
       "id": 'C',
       "icon": "fa fa-folder"
   }];

Note: ParentName I've included to identify hierarchy of folder structure. ID will be same as of name.

Any suggestion for this?

Thanks..

share|improve this question
1  
step 1- var array = JSON.parse(your string goes here) - then at least you have a JS array you can work with - the next bit is harder though – Jaromanda X 2 days ago
    
Yes, I'm stuck in next part... – user7417866 2 days ago
    
You changed the required output in an essential way. Are you really sure you need it like that? What is the purpose to have items as array when you add essentially the same node there as the parent, and only add the children in the next level? This seems like a bad designed structure. – trincot 2 days ago
    
@trincot I'm trying to integrate navigation menu from multi-level-push-menu.make.rs into my project requirement for dynamic folder structure navigation, this plugin required JS array in this way. – user7417866 2 days ago
up vote 0 down vote accepted

You could use a Map to key the nodes by name, and build the tree while iterating over the input with reduce. For each node create a parent node if it does not yet exist. When this happens, remember this newly created parent as the root of the tree: its children are the array you want to produce.

Here is the ES6 code:

// Sample input JSON parsed:
const items = JSON.parse('[{"Name":"A","ParentName":"Config","Type":"default"},{"Name":"new","ParentName":"A","Type":"file"},{"Name":"B","ParentName":"Config","Type":"default"},{"Name":"C","ParentName":"Config","Type":"default"}]');

const arr = items.reduce( ([nodes, root], {Name, ParentName, Type}) => {
    const node = Object.assign({ // create node
        name: Name,
        icon: Type == 'default' ? 'fa fa-folder' : 'fa fa-file'
    }, nodes.get(Name)); // add previously registered children, if any
    const parent = nodes.get(ParentName) || (root = {}); // create parent if not present
    parent.items = (parent.items || []).concat(node); // add current as child
    return [nodes.set(Name, node).set(ParentName, parent), root];
}, [new Map, {}] )[1].items; // start with empty map, return the items of the root

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

Update after updated question

In the update of your question the desired output was changed, having more nested levels: nodes with children now need an intermediate object as child (with mostly the same properties) which in turn has the children nodes attached to its own items property.

Here is the ES6 code adapted for that purpose:

function buildTree(folders) {
    const [nodes, root] = folders.reduce( ([nodes, root], {Name, ParentName, Type}) => {
        const node = Object.assign({ // create node
            name: Name,
            id: Name,
            icon: Type == 'default' ? 'fa fa-folder' : 'fa fa-file'
        }, nodes.get(Name)); // add previously registered children, if any
        const parent = nodes.get(ParentName) || (root = {}); // create parent if not present
        parent.items = (parent.items || []).concat(node); // add current as child
        return [nodes.set(Name, node).set(ParentName, parent), root];
    }, [new Map, {}] );
    // To add the extra intermediate levels (requested in updated question):
    nodes.forEach( node => {
        if (node.items) node.items = [{
            title: node.name,
            icon: node.icon,
            id: node.id,
            items: node.items
        }]
    });
    return root.items[0].items;
}    

// Sample JSON data, parsed
const folders = JSON.parse('[{ "Name": "A", "ParentName": "Config", "Type": "default" },{ "Name": "SubA", "ParentName": "A", "Type": "default" },{ "Name": "SubAFile", "ParentName": "SubA", "Type": "file" },{ "Name": "B", "ParentName": "Config", "Type": "default" },{ "Name": "C", "ParentName": "Config", "Type": "default" }]');

const arr = buildTree(folders);

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

share|improve this answer
    
Thanks for your code, it helps.. however I discover new edits required to be made, if you could help? – user7417866 2 days ago
    
Sure, but your last update to the question breaks the logic. How do the input and the output relate now? It is not really fair to change the question in such essential way, when people have spent time on solving the original question. Also, it is not really an id any more when it can have duplicates. – trincot 2 days ago
    
apology for change and not having closer look at requirement. Id is same as of name and its not gonna be same, it will be into title element only repeating. I'm trying to integrate navigation menu from multi-level-push-menu.make.rs into my code for folder structure navigation. – user7417866 2 days ago
    
I added a new section to my answer, which produces the new structure. – trincot 2 days ago
    
Thanks Ton Sir.. this works perfect.. Appreciated you help :) :) – user7417866 2 days ago

First use JSON.parse for generating an obbject from a valid JSON string.

The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned.

Then you could use an iterative approach for generating a tree with creating a new object with the wanted properties for referencing inserted or referenced parent objects, a temporary object is used.

This works for unsorted and nested items as well.

var data = [{ Name: "A", ParentName: "Config", Type: "default" }, { Name: "SubA", ParentName: "A", Type: "default" }, { Name: "SubAFile", ParentName: "SubA", Type: "file" }, { Name: "B", ParentName: "Config", Type: "default" }, { Name: "C", ParentName: "Config", Type: "default" }],
    tree = function (data, root) {
        var r = [], o = {};
        data.forEach(function (a) {
            var temp = { name: a.Name, icon: a.Type === 'file' ? 'fa fa-file' : 'fa fa-folder' };
            if (o[a.Name] && o[a.Name].items) {
                temp.items = o[a.Name].items;
            }
            o[a.Name] = temp;
            if (a.ParentName === root) {
                r.push(temp);
            } else {
                o[a.ParentName] = o[a.ParentName] || {};
                o[a.ParentName].items = o[a.ParentName].items || [];
                o[a.ParentName].items.push(temp);
            }
        });
        return r;
    }(data, 'Config');

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

share|improve this answer
    
Thanks a ton, this was helpful, but there can be a multiple folder into a root folder under config – user7417866 2 days ago
    
Thanks for edit it works fine, however I discover some more edits while integrating your solutions, if you could help... sorry for not having closer look over required input earlier wile posting question.. – user7417866 2 days ago

Such a great quiz! I'll promise you to find solution in a couple of hours. What you gave is a kind of "reverse binary tree" and the current solution in terms of BigO looks too ugly by my own.

If you don't mind I will post draft preSolution and keep thinking about more right way to increase productivity and edit it a bit later.

var tree = {}

function findParent(data, parentName){
    var parentExist = false;

    $.each(data, function(index, value){
        parentExist = (value.name == parentName);

        if(parentExist){
            moveChild(data, index, tree, tree[value.parentName]);
        } else {
            createParent(parentName, tree);
        }
    }
}

function moveChild(collectionIn, child, collectionOut, parent){
    collectionOut[parent].push(collectionIn[child]);
    splice(collectionIn[child], 1);
}

function createParent(parentName, targetTree);

$.each(data, function(index, val){
  findParent(index.parentName);
  
});

tips I'll got to check:

share|improve this answer
    
Thanks for your code... – user7417866 2 days ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.