2013-04-03 14 views
14

Qualcuno può aiutare a convertire il seguente elenco di oggetti padre-figlio:Convert genitore-figlio array ad albero

 
[ 
    { 
     "name":"root", 
     "_id":"root_id", 
    }, 
    { 
     "name":"a1", 
     "parentAreaRef":{ 
     "id":"root_id", 
     }, 
     "_id":"a1_id", 
    }, 
    { 
     "name":"a2", 
     "parentAreaRef":{ 
     "id":"a1_id", 
     }, 
     "_id":"a2_id", 
    }, 
    { 
     "name":"a3", 
     "parentAreaRef":{ 
     "id":"a2_id", 
     }, 
     "_id":"a3_id", 
    }, 
    { 
     "name":"b1", 
     "parentAreaRef":{ 
     "id":"root_id", 
     }, 
     "_id":"b1_id", 
    }, 
    { 
     "name":"b2", 
     "parentAreaRef":{ 
     "id":"b1_id", 
     }, 
     "_id":"b2_id", 
    }, 
    { 
     "name":"b3", 
     "parentAreaRef":{ 
     "id":"b1_id", 
     }, 
     "_id":"b3_id", 
    } 
]

in una struttura ad albero che mostra la relazione genitore-figlio:

 
[ 
    { 
     "name": "root", 
     "_id":"root_id", 
     "children": [ 
      { 
       "name": "a1", 
       "_id":"a1_id", 
       "children" : [ 
        { 
         "name" : "a2", 
         "_id":"a2_id", 
         "children" : [ 
          { 
           "name" : "a3" 
           "_id":"a3_id" 
          } 
         ] 
        } 
       ] 
      }, 
      { 
       "name": "b1", 
       "_id":"b1_id", 
       "children" : [ 
        { 
         "name" : "b2" 
         "_id":"b2_id" 
        }, 
        { 
         "name" : "b3" 
         "_id":"b3_id" 
        } 
       ] 
      } 
     ] 
    } 
] 

(The la struttura di output è una matrice che consente più radici ma se è possibile ottenere una soluzione che gestisca una singola radice è anche ottima.)

L'albero di output è simile al seguente:

 

root 
    | 
    -- a1 
    | | 
    | -- a2 
    |  | 
    |  -- a3 
    | 
    -- b1 
     | 
     -- b2 
     -- b3 


Grazie!

+0

sacco :) ma (ovviamente) non ancora arrivati ​​ad una soluzione. Posso pubblicare alcuni frammenti di codice su cui ho lavorato ma penso che causeranno più confusione che chiarezza –

+1

@Scobal, per favore pubblica i frammenti.Potresti essere molto vicino alla soluzione e potremmo dirti quale sia la soluzione. –

+0

Avrete bisogno di analisi e mappatura. –

risposta

23

Ho una soluzione che funziona. Posso darti suggerimenti per risolverlo. La cosa buona è che i tuoi dati non contengono alcun riferimento futuro ai nodi. Quindi puoi creare il tuo albero con un solo passaggio attraverso l'array. Se nota, dovrai prima passare attraverso l'intero array per creare una mappa di ID ai nodi.

Il tuo algoritmo sarà simile a questo.

  1. Creare una mappa che associa gli ID ai nodi. Ciò renderà più semplice la ricerca dei nodi.
  2. Passa attraverso l'array di nodi.
  3. Per ogni elemento.
    1. Aggiungere una voce nella mappa.
    2. Aggiungere una proprietà children (un array) a questo nodo.
    3. L'elemento ha un genitore? In caso contrario, deve essere la radice, quindi assegnare questo elemento alla radice dell'albero.
    4. Questo elemento ha un genitore, quindi cerca il nodo genitore, quindi aggiungi questo nodo corrente come figlio del nodo genitore (aggiungilo all'array children).

Questo dovrebbe aiutare a risolvere il problema. Se stai riscontrando problemi specifici con questo algoritmo, posso indicare dove sono i problemi e come risolverlo o pubblicare la soluzione e spiegare come l'ho risolto.

UPDATE

ho guardato la soluzione che avete. In realtà non hai bisogno di ricorsione per questo e puoi farlo in modo iterativo usando l'algoritmo che ho descritto sopra. Stai anche modificando la struttura sul posto, il che rende l'algoritmo più complicato. Ma sei un po 'sulla buona strada. Ecco come ho risolto:

var idToNodeMap = {}; //Keeps track of nodes using id as key, for fast lookup 
var root = null; //Initially set our loop to null 

//loop over data 
data.forEach(function(datum) { 

    //each node will have children, so let's give it a "children" poperty 
    datum.children = []; 

    //add an entry for this node to the map so that any future children can 
    //lookup the parent 
    idToNodeMap[datum._id] = datum; 

    //Does this node have a parent? 
    if(typeof datum.parentAreaRef === "undefined") { 
     //Doesn't look like it, so this node is the root of the tree 
     root = datum;   
    } else {   
     //This node has a parent, so let's look it up using the id 
     parentNode = idToNodeMap[datum.parentAreaRef.id]; 

     //We don't need this property, so let's delete it. 
     delete datum.parentAreaRef; 

     //Let's add the current node as a child of the parent node. 
     parentNode.children.push(datum);   
    } 
}); 

ora root punti per l'intero albero.

Fiddle.

Per il caso in cui la matrice di elementi è in ordine arbitrario, è necessario inizializzare prima idToNodeMap.Il resto dell'algoritmo rimane più o meno lo stesso (tranne che per la linea in cui si memorizza il nodo della mappa, che non è necessaria perché l'avete fatto già nel primo passaggio):

var idToNodeMap = data.reduce(function(map, node) { 
    map[node._id] = node; 
    return map; 
}, {}); 
+0

Cool, grazie mille! –

+0

Solo per informazioni, il violino corrente restituisce un errore di tipo 'Uncaught TypeError: Impossibile leggere la proprietà '# ' di undefined'. Altrimenti, mi piace l'implementazione, semplice e concisa, +1. –

+0

@ LasseChristiansen-sw_lasse Oops; Ho collegato a una versione precedente. Ho risolto il link. –

0

Give it una prova:

var obj = {}; 
    obj.rootElements = []; 
    var currentRoot; 
    var currentParent; 
    for (s in a) { 
     var t = a[s]; 
     var id = t._id; 
     if (t.parentAreaRef) { 
      var parentId = t.parentAreaRef.id; 
      if (parentId == currentParent._id) { 
       //add children 
       if (!currentParent.children) { 
        currentParent.children = []; 
       } 
       currentParent.children.push(t); 
      } 
      else { 
       addChildToParent(t, parentId); 
      } 

     } 
     else // is root 
     { 
      currentRoot = t; 
      currentParent = t; 
      obj.rootElements.push(currentRoot); 
     } 
    } 

    var t = currentRoot 

    function addChildToParent(child, parentId, root) { 
     for (p in a) { 
      if (a[p]._id.toString() == parentId.toString()) { 
       if (!a[p].children) { 
        a[p].children = []; 
       } 
       a[p].children.push(t); 
      } 
     } 
    } 
2

so di essere troppo tardi, ma da quando ho appena finito il mio contributo ad un esempio di implementazione di come questo può essere fatto ho pensato di condividerlo, in quanto potrebbe essere trovato utile/o dare ispirazione a una soluzione alternativa.

L'applicazione può essere trovato qui: http://jsfiddle.net/sw_lasse/9wpHa/

L'idea principale dei centri di implementazione in tutto il seguente funzione ricorsiva:

// Get parent of node (recursive) 
var getParent = function (rootNode, rootId) { 

    if (rootNode._id === rootId) 
     return rootNode; 

    for (var i = 0; i < rootNode.children.length; i++) { 
     var child = rootNode.children[i]; 
     if (child._id === rootId) 
      return child; 

     if (child.children.length > 0) 
      var childResult = getParent(child, rootId); 

     if (childResult != null) return childResult; 
    } 
    return null; 
}; 

... che viene utilizzato per costruire l'albero.

+0

Bello, grazie comunque :) –

2

Lo so che è tardi, ma ho appena finito questo algoritmo e forse posso aiutare altre persone che cercano di risolvere lo stesso problema: http://jsfiddle.net/akerbeltz/9dQcn/

Una cosa buona è che non richiede alcun tipo speciale sull'oggetto originale.

Se avete bisogno di adattarlo alle vostre esigenze cambiano le seguenti righe:

  1. Modificare il _id e il parentAreaRef.id a seconda della struttura.

    if (String(tree[i]._id) === String(item.parentAreaRef.id)) {

  2. Modificare il parentAreaRef a seconda della struttura.

    if (tree[idx].parentAreaRef) buildTree(tree, tree.splice(idx, 1)[0])

Speranza che aiuta!

+0

Questo è stato utile per me. Grazie – Enkode

0

C'è un errore nella stringa di

a[p].children.push(t); 

Dovrebbe essere

a[p].children.push(child); 

anche io sono poco ottimizzarlo:

var data = [{"id":1,"name":"X","parentId":null},{"id":2,"name":"Y","parentId":1},{"id":3,"name":"D","parentId":2},{"id":2,"name":"S","parentId":1},{"id":5,"name":"K","parentId":4}] 
    var obj = {}; 
    obj.rootElements = []; 
    for (i in data) { 
     var _elem = data[i]; 
     if (_elem.parentId) { 
      var _parentId = _elem.parentId; 
      if (_parentId == _elem.id) { 
       // check children, if false - add 
       if (!_elem.children) { 
        _elem.children = []; 
       } 
       _elem.children.push(_elem); 
      } 
      else { 
       addChildToParent(_elem, _parentId); 
      } 
     } 
     else // is root 
     { 
      obj.rootElements.push(_elem); 
     } 
    } 
    function addChildToParent(child, parentId, root) { 
     for (j in data) { 
      if (data[j].id.toString() == parentId.toString()) { 
       if (!data[j].children) { 
        data[j].children = []; 
       } 
       data[j].children.push(child); 
      } 
     } 
    } 
    res.send(obj.rootElements); 
1

È possibile utilizzare il modulo array-to-tree da NPM .

+1

Oppure usa la mia implementazione più performante (unità testata, copertura del codice al 100%, solo 0,5 kb di dimensione e include tipi di tipizzazione): npmjs.com/package/performant-array-to-tree –

0

Prendendo in prestito la logica di memorizzazione nella cache dalla risposta di Vivin Paliath, ho creato una funzione riutilizzabile per convertire un elenco di dati con relazioni figlio-genitore in un albero.

var data = [ 
 
    { "id" : "root"      }, 
 
    { "id" : "a1", "parentId" : "root", }, 
 
    { "id" : "a2", "parentId" : "a1", }, 
 
    { "id" : "a3", "parentId" : "a2", }, 
 
    { "id" : "b1", "parentId" : "root", }, 
 
    { "id" : "b2", "parentId" : "b1", }, 
 
    { "id" : "b3", "parentId" : "b1", } 
 
]; 
 
var options = { 
 
    childKey : 'id', 
 
    parentKey : 'parentId' 
 
}; 
 
var tree = walkTree(listToTree(data, options), pruneChildren); 
 

 
document.body.innerHTML = '<pre>' + JSON.stringify(tree, null, 4) + '</pre>'; 
 

 
function listToTree(list, options) { 
 
    options = options || {}; 
 
    var childKey = options.childKey || 'child'; 
 
    var parentKey = options.parentKey || 'parent'; 
 
    var childrenKey = options.childrenKey || 'children'; 
 
    var nodeFn  = options.nodeFn  || function(node, name, children) { 
 
    return { name : name, children : children }; 
 
    }; 
 
    var nodeCache = {}; 
 
    return list.reduce(function(tree, node) { 
 
    node[childrenKey] = []; 
 
    nodeCache[node[childKey]] = node; 
 
    if (typeof node[parentKey] === 'undefined' || node[parentKey] === '') { 
 
     tree = nodeFn(node, node[childKey], node[childrenKey]); 
 
    } else { 
 
     parentNode = nodeCache[node[parentKey]]; 
 
     parentNode[childrenKey].push(nodeFn(node, node[childKey], node[childrenKey])); 
 
    } 
 
    return tree; 
 
    }, {}); 
 
} 
 

 
function walkTree(tree, visitorFn, parent) { 
 
    if (visitorFn == null || typeof visitorFn !== 'function') { 
 
    return tree; 
 
    } 
 
    visitorFn.call(tree, tree, parent); 
 
    if (tree.children && tree.children.length > 0) { 
 
    tree.children.forEach(function(child) { 
 
     walkTree(child, visitorFn, tree); 
 
    }); 
 
    } 
 
    return tree; 
 
} 
 

 
function pruneChildren(node, parent) { 
 
    if (node.children.length < 1) { 
 
    delete node.children; 
 
    } 
 
}

+0

Una domanda correlata, in PHP, può essere trovato qui: [Convertire una serie di relazioni genitore-figlio in un albero gerarchico?] (http://stackoverflow.com/questions/2915748) –