2

Given the following JSON Array:

    [{"ID":12,"NAME":"ktc","PARENTID":0},
     {"ID":11,"NAME":"root","PARENTID":0}, 
     {"ID":1,"NAME":"rwhitney","PARENTID":0},
     {"ID":21,"NAME":"shared folder","PARENTID":0}, 
     {"ID":2,"NAME":".config","PARENTID":1}, 
     {"ID":5,"NAME":"wallpapers","PARENTID":1}, 
     {"ID":3,"NAME":"geany","PARENTID":2}, 
     {"ID":4,"NAME":"colorschemes","PARENTID":3}, 
     {"ID":13,"NAME":"efast","PARENTID":12}, 
     {"ID":15,"NAME":"includes","PARENTID":13}, 
     {"ID":14,"NAME":"views","PARENTID":13}, 
     {"ID":17,"NAME":"css","PARENTID":15}, 
     {"ID":16,"NAME":"js","PARENTID":15}]

I need to build a menu tree with the subfolders nested beneath the parent folders.

Here is some server side code:

    socket.on('get-folders', function(data){
        var folders = [];
        getSession(session.key, function(currSession){
            db.rows('getFolders', currSession, [currSession.user], function(err, rows){
                if (err) {
                    socket.emit('err', 'Error is: ' + err );
                } else if(rows[0]){
                    //~ folders.push(JSON.stringify(rows));
                    socket.emit('get-folders', JSON.stringify(rows));
                    //~ n_Folders(rows, currSession, socket, folders, 0);
                }
            });
        });
    });

and client side:

    function rtnSocket(cmd, data, cb){
        socket.emit(cmd, data);
        socket.on(cmd, cb);
    }

    rtnSocket('get-folders', folderid, function(data){
        console.log(data);
    });

can someone please help guide me in the right direction?

6
  • it's usually better to use an existing library for this. Commented Feb 25, 2020 at 7:27
  • Thanks, but I need to learn the mechanics of a tree-like menu. And I don't want to use a library, if I can help it. Commented Feb 25, 2020 at 7:30
  • Ok, I was thinking it is for some real project. (I that case it would loose a lot of time). Commented Feb 25, 2020 at 7:32
  • Why do you need server side for a tree menu? Commented Feb 25, 2020 at 7:32
  • security, it is for a real project Commented Feb 25, 2020 at 7:33

4 Answers 4

2

You could collect all nodes from a flat data structure, use the ID and PARENTID as keys in a hash table and get the root array as result.

var data = [{ ID: 12, NAME: "ktc", PARENTID: 0 }, { ID: 11, NAME: "root", PARENTID: 0 }, { ID: 1, NAME: "rwhitney", PARENTID: 0 }, { ID: 21, NAME: "shared folder", PARENTID: 0 }, { ID: 13, NAME: "efast", PARENTID: 12 }, { ID: 2, NAME: ".config", PARENTID: 1 }, { ID: 5, NAME: "wallpapers", PARENTID: 1 }, { ID: 15, NAME: "includes", PARENTID: 13 }, { ID: 14, NAME: "views", PARENTID: 13 }, { ID: 3, NAME: "geany", PARENTID: 2 }, { ID: 17, NAME: "css", PARENTID: 15 }, { ID: 16, NAME: "js", PARENTID: 15 }, { ID: 4, NAME: "colorschemes", PARENTID: 3 }],
    tree = function (data, root) {
        var t = {};
        data.forEach(o => {
            Object.assign(t[o.ID] = t[o.ID] || {}, o);
            t[o.PARENTID] = t[o.PARENTID] || {};
            t[o.PARENTID].children = t[o.PARENTID].children || [];
            t[o.PARENTID].children.push(t[o.ID]);
        });
        return t[root].children;
    }(data, 0);

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

Sign up to request clarification or add additional context in comments.

6 Comments

I have opted for a single query which gets the results into a usable JSON object. Before it was an array of objects and seemed clunky.
what does it mean? please edit the question, if necessary.
Nina, I just updated my question to reflect what I am using now. I now need to convert the JSON into a visual hierarchical tree. Can you offer any advice on that?
you could ask another question, because for a visual appearance, you need some style and i would need an example of it.
I tried asking another question, but it was almost closed immediately so I deleted it. Apparently I'm no good at asking questions. :(
|
0

Its better to put the subFolders in inside the Parent Object as SubFolder Array .. it will eliminate ParentID and make easy to traverse

[
  {
    "ID": 1,
    "NAME": "rwhitney",
    "PARENTID": 0,
    "SUB": [
      {
        "ID": 2,
        "NAME": ".config",
        "PARENTID": 1,
        "SUB": [
          {
            "ID": 3,
            "NAME": "geany",
            "PARENTID": 2,
            "SUB": [
              {
                "ID": 4,
                "NAME": "colorschemes",
                "PARENTID": 3
              }
            ]
          }
        ]
      },
      {
        "ID": 5,
        "NAME": "wallpapers",
        "PARENTID": 1
      }
    ]
  }
]

1 Comment

The question was about how to do that. Presumably the array structure supplied is available somewhere, and now there is a reason to convert it to a tree.
0

At first we need to flat nested array:

const flatArray = (arr) => {
    return arr.reduce((flat, toFlatten) => {
        return flat.concat(Array.isArray(toFlatten) ? flatArray(toFlatten) : toFlatten);
    }, []);
}

Then we can create a tree:

const makeTree = dataset => {
    let hashTable = Object.create(null)
    dataset.forEach( aData => hashTable[aData.ID] = { ...aData, childNodes : [] } )
    let dataTree = []
    dataset.forEach( aData => {
      if( aData.PARENTID ) hashTable[aData.PARENTID].childNodes.push(hashTable[aData.ID])
      else dataTree.push(hashTable[aData.ID])
    } )
    return dataTree
}

An example:

let data = [
    [{ ID: 12, NAME: "ktc", PARENTID: 0 }, { ID: 11, NAME: "root", PARENTID: 0 }, { ID: 1, NAME: "rwhitney", PARENTID: 0 },
    { ID: 21, NAME: "shared folder", PARENTID: 0 }], [{ ID: 13, NAME: "efast", PARENTID: 12 }], [{ ID: 2, NAME: ".config", PARENTID: 1 },
    { ID: 5, NAME: "wallpapers", PARENTID: 1 }], [{ ID: 15, NAME: "includes", PARENTID: 13 }, { ID: 14, NAME: "views", PARENTID: 13 }],
    [{ ID: 3, NAME: "geany", PARENTID: 2 }], [{ ID: 17, NAME: "css", PARENTID: 15 }, { ID: 16, NAME: "js", PARENTID: 15 }],
    [{ ID: 4, NAME: "colorschemes", PARENTID: 3 }]];

const flatArray = (arr) => {
    return arr.reduce((flat, toFlatten) => {
        return flat.concat(Array.isArray(toFlatten) ? flatArray(toFlatten) : toFlatten);
    }, []);
}

const makeTree = dataset => {
    let hashTable = Object.create(null)
    dataset.forEach( aData => hashTable[aData.ID] = { ...aData, childNodes : [] } )
    let dataTree = []
    dataset.forEach( aData => {
      if( aData.PARENTID ) hashTable[aData.PARENTID].childNodes.push(hashTable[aData.ID])
      else dataTree.push(hashTable[aData.ID])
    } )
    return dataTree
}
const dataTree = makeTree(flatArray(data));
console.log(dataTree)

Comments

0

I need to answer my own question with the help of Nina above:

With the given JSON object - answer from Nina:

[{"ID":12,"NAME":"ktc","PARENTID":0},{"ID":11,"NAME":"root","PARENTID":0},{"ID":1,"NAME":"rwhitney","PARENTID":0},{"ID":21,"NAME":"shared folder","PARENTID":0},{"ID":2,"NAME":".config","PARENTID":1},{"ID":5,"NAME":"wallpapers","PARENTID":1},{"ID":3,"NAME":"geany","PARENTID":2},{"ID":4,"NAME":"colorschemes","PARENTID":3},{"ID":13,"NAME":"efast","PARENTID":12},{"ID":15,"NAME":"includes","PARENTID":13},{"ID":14,"NAME":"views","PARENTID":13},{"ID":17,"NAME":"css","PARENTID":15},{"ID":16,"NAME":"js","PARENTID":15},{"ID":27,"NAME":"images","PARENTID":16}]

I came up with this function:

var LHR= '',folderid = 0, parentid = 0;

    var seg = location.pathname.split('/')[2];
    if(seg){
        LHR = seg.split('_')[0];
        folderid = seg.split('_')[1] || 0;
        //~ alert(folderid);
        parentid = seg.split('_')[2] || 0;
        if(isLike(LHR,['share']) == true){
            sharedFileID = LHR.split('-')[1];
        }
    }


LHR = LHR.replace(/%20/g,' ');
var MLHR = isLike(LHR, ['share']) == true ? LHR.split('-')[0] : LHR;
var folders = '';
function recurse(data, indent, limit){
  if(limit < 10){
    for(var i = 0;i<data.length;i++){
        if(folderid == data[i].ID){
            folders += '<div><input style="margin-left:60px" type="checkbox" data-id="'+folderid+'" data-name="' + data[i].NAME + '" class="check-folder tooltip">'+
                '<img style="margin-left:0px" data-pid="'+parentid+'" id="folder-'+folderid+'" src="/fa/folder-open.svg" class="blk tooltip"> ' + MLHR.replace(/%20/g,' ') + ' </div>';
        } else {
            folders += '<input type="checkbox" style="margin-left:'+indent+'px" data-id="'+data[i].ID+'" data-name="' + data[i].NAME + '" class="check-folder tooltip">'+
                '<a style="margin-left:70px" ondrop="drop(event)" ondragover="allowDrop(event)" class="dsp-ib w150 blk folders drop drag" draggable="true" droppable="true"  href="/dashboard/'+data[i].NAME+'_'+data[i].ID+'_'+data[i].PARENTID+'">'+
                '<img data-pid="'+data[i].PARENTID+'" src="/fa/folder.svg" class="fa ml--80 blk dsp-ib" id="folder-'+data[i].ID+'"> ' + data[i].NAME + '</a><br>';
        }
      if(data[i].children){
          recurse(data[i].children, indent+=20, ++limit);
      }
    }
  }

  $('#folders').html(folders);
}

and call it like thus:

recurse(data,0,0);

The function populates the tree of id "folders"

with the following output:

enter image description here

Thanks again for setting me on the right track!

Comments

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.