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?
-
it's usually better to use an existing library for this.V. Sambor– V. Sambor2020年02月25日 07:27:20 +00:00Commented 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.richardwhitney– richardwhitney2020年02月25日 07:30:11 +00:00Commented 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).V. Sambor– V. Sambor2020年02月25日 07:32:08 +00:00Commented Feb 25, 2020 at 7:32
-
Why do you need server side for a tree menu?V. Sambor– V. Sambor2020年02月25日 07:32:59 +00:00Commented Feb 25, 2020 at 7:32
-
security, it is for a real projectrichardwhitney– richardwhitney2020年02月25日 07:33:56 +00:00Commented Feb 25, 2020 at 7:33
4 Answers 4
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; }
6 Comments
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
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
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:
Thanks again for setting me on the right track!
Comments
Explore related questions
See similar questions with these tags.