1
votes

Construire une arborescence à partir d'un tableau d'objets JSON

Étant donné le tableau JSON suivant:

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

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

Je dois créer une arborescence de menus avec les sous-dossiers imbriqués sous les dossiers parents.

Voici un code côté serveur:

    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);
                }
            });
        });
    });

et côté client:

    [{"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}]

quelqu'un peut-il s'il vous plaît m'aider à me guider dans la bonne direction?


6 commentaires

il est généralement préférable d'utiliser une bibliothèque existante pour cela.


Merci, mais j'ai besoin d'apprendre les mécanismes d'un menu en forme d'arbre. Et je ne veux pas utiliser une bibliothèque, si je peux l'aider.


Ok, je pensais que c'était pour un vrai projet. (Dans ce cas, il perdrait beaucoup de temps).


Pourquoi avez-vous besoin du côté serveur pour un menu arborescent?


la sécurité, c'est pour un vrai projet


il est préférable de mettre les sous-dossiers à l'intérieur de l'objet parent en tant que tableau de sous-dossiers .. cela éliminera ParentID et facilitera la traversée


4 Réponses :


2
votes

Vous pouvez collecter tous les nœuds à partir d'une structure de données plate, utiliser ID et PARENTID comme clés dans une table de hachage et obtenir le tableau racine comme résultat.

.as-console-wrapper { max-height: 100% !important; top: 0; }
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);


6 commentaires

J'ai opté pour une seule requête qui obtient les résultats dans un objet JSON utilisable. Avant, c'était un tableau d'objets et semblait maladroit.


Qu'est-ce que ça veut dire? veuillez modifier la question, si nécessaire.


Nina, je viens de mettre à jour ma question pour refléter ce que j'utilise maintenant. Je dois maintenant convertir le JSON en une arborescence hiérarchique visuelle. Pouvez-vous donner des conseils à ce sujet?


vous pourriez poser une autre question, car pour une apparence visuelle, vous avez besoin d'un certain style et j'en aurais besoin d'un exemple.


J'ai essayé de poser une autre question, mais elle a été presque fermée immédiatement, alors je l'ai supprimée. Apparemment, je ne suis pas doué pour poser des questions. :(


Je pense que je suis sur quelque chose de toute façon! Merci



0
votes

Il est préférable de placer les sous-dossiers à l'intérieur de l'objet parent en tant que tableau de sous-dossiers .. cela éliminera ParentID et facilitera la traversée

[
  {
    "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 commentaires

La question était de savoir comment faire cela. Vraisemblablement, la structure de tableau fournie est disponible quelque part, et il y a maintenant une raison de la convertir en arbre.



0
votes

Au début, nous devons mettre à plat un tableau imbriqué:

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)

Ensuite, nous pouvons créer un arbre:

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
}

Un exemple: p>

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


0 commentaires

0
votes

Je dois répondre à ma propre question avec l'aide de Nina ci-dessus:

Avec l'objet JSON donné - réponse de Nina:

recurse(data,0,0);

J'ai trouvé ceci fonction:

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);
}

et appelez-la comme ceci:

[{"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}]

La fonction remplit l'arborescence des "dossiers" id p>

avec la sortie suivante:

 entrez la description de l'image ici

Merci encore de m'avoir mis sur la bonne voie!


0 commentaires