2010-02-16 9 views
7

Sto tentando di impostare un elenco che può avere più livelli, utilizzando parentId per definire il suo genitore. Il primo articolo parentId è NULL. Esempio di alcune voci:Creazione di un array multilivello utilizzando parentIds in PHP

id parentId name 
1 NULL  item1 
2 NULL  item2 
3 1  item3 
4 2  item4 
5 3  item5 
6 3  item6 

Così, 1 e 2 sono gli elementi principali; 3 è un bambino di 1; 4 è un bambino di 2; 5 è un figlio di 3 (che è figlio di 1 stesso); 6 è anche un figlio di 3 (che è figlio di 1 stesso); ecc.

Sono bloccato creando un array che aggiunge correttamente questi elementi ai livelli corretti. Esso dovrebbe essere simile a questo:


Array 
(
    [1] => Array 
     (
      [name] => item1 
      [parentId] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [name] => item3 
          [parentId] => 1 
          [children] => Array 
           (
            [5] => Array 
             (
              [name] => item5 
              [parentId] => 3 
             ) 

            [6] => Array 
             (
              [name] => item6 
              [parentId] => 3 
             ) 

           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [name] => item2 
      [parentId] => 
      [children] => Array 
       (
        [4] => Array 
         (
          [name] => item4 
          [parentId] => 2 
         ) 

       ) 

     ) 

)

Ma dire che passare attraverso tutti gli elementi utilizzando foreach(), e ottengo al punto 5. La sua parentId è 3, ma a quel punto, non ho idea di dove questo genitore 3 è situato nell'array e come aggiungere figli a quel genitore.

C'è un trucco per passare in rassegna questi articoli e metterli tutti sul posto nel modo giusto?

risposta

11

qui va

// your original data as an array 
$data = array(
    array(
     'id' => 1, 
     'parentId' => null, 
     'name' => 'item1' 
    ), 
    array(
     'id' => 2, 
     'parentId' => null, 
     'name' => 'item2' 
    ), 
    array(
     'id' => 3, 
     'parentId' => 1, 
     'name' => 'item3' 
    ), 
    array(
     'id' => 4, 
     'parentId' => 2, 
     'name' => 'item4' 
    ), 
    array(
     'id' => 5, 
     'parentId' => 3, 
     'name' => 'item5' 
    ), 
    array(
     'id' => 6, 
     'parentId' => 3, 
     'name' => 'item6' 
    ), 
); 

Una funzione ricorsiva

function buildTree($ar, $pid = null) { 
    $op = array(); 
    foreach($ar as $item) { 
     if($item['parentId'] == $pid) { 
      $op[$item['id']] = array(
       'name' => $item['name'], 
       'parentId' => $item['parentId'] 
      ); 
      // using recursion 
      $children = buildTree($ar, $item['id']); 
      if($children) { 
       $op[$item['id']]['children'] = $children; 
      } 
     } 
    } 
    return $op; 
} 

print_r(buildTree($data)); 

/* 
Array 
(
    [1] => Array 
     (
      [name] => item1 
      [parentId] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [name] => item3 
          [parentId] => 1 
          [children] => Array 
           (
            [5] => Array 
             (
              [name] => item5 
              [parentId] => 3 
             ) 

            [6] => Array 
             (
              [name] => item6 
              [parentId] => 3 
             ) 

           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [name] => item2 
      [parentId] => 
      [children] => Array 
       (
        [4] => Array 
         (
          [name] => item4 
          [parentId] => 2 
         ) 

       ) 

     ) 

) 
*/ 
+0

Grazie! Esattamente quello che stavo cercando. La parte ricorsiva era il trucco. – Alec

+0

Molto buono! Ma questo diventa estremamente lento quando hai 20.000 set di dati e 10 livelli di profondità. – Sliq

1

Si dovrebbe utilizzare l'ID di un elemento come valore chiave per la matrice, in modo da poter aggiungere un elemento a suo genitore in questo modo:

$array[$parentID]['children'][$childID] = array(); 
1

La prima cosa che viene in mente è solo una versione piatta di quello che hai:

array (
[0] => array(
    'name' => 'item1', 
    'parent' => null 
    ), 
[1] => array(
    'name' => 'item2', 
    'parent' => null 
    ), 
[3] => array(
    'name' => 'item3', 
    'parent' => 0 
    ), 
[4] => array(
    'name' => 'item4', 
    'parent' => 3 
    ), 
[5] => array(
    'name' => 'item5', 
    'parent' => 1 
    ), 
[6] => array(
    'name' => 'item6', 
    'parent' => 1 
    ),); 

Fondamentalmente, si fa sempre riferimento al genitore. Per trovare tutti i bambini, dovresti scorrere l'array. Il tempo di installazione iniziale sarebbe piuttosto veloce, però.

La seconda che viene in mente, e comporterebbe una messa a punto molto di più, ma molto meno tempo di accesso più tardi:

array (
[0] => array(
    'name' => 'item1', 
    'parent' => null, 
    'children' = array(3) 
    ), 
[1] => array(
    'name' => 'item2', 
    'parent' => null 
    'children' = array(5, 6) 
    ), 
[3] => array(
    'name' => 'item3', 
    'parent' => 0 
    'children' = array(4) 
    ), 
[4] => array(
    'name' => 'item4', 
    'parent' => 3 
    'children' = array() 
    ), 
[5] => array(
    'name' => 'item5', 
    'parent' => 1 
    'children' = array() 
    ), 
[6] => array(
    'name' => 'item6', 
    'parent' => 1 
    'children' = array() 
    ),); 

In questo, devi essere l'aggiunta di tutti gli indici di bambino il genitore. Ci sarebbe voluto un po 'di più, ma i tempi di accesso successivi sarebbero stati rapidi. Mentre stai cercando di capire i genitori, devi semplicemente aggiungere alla matrice dei figli dei genitori.

L'unico vero svantaggio del secondo approccio è che se si desidera aggiungere o rimuovere un elemento, è necessario ricordare di tornare indietro e aggiornare l'array figlio per il genitore.

0

Potrebbe non essere necessario una nuova soluzione di più, ma questo potrebbe rivelarsi utile per altri utenti:

// ! assuming $elements is built like: Array(id1 => Array(/*parameters*/) , ...) 

function buildTree($elements) { 

    $e = Array(0 => Array()); // elements array 
    $r = Array(0 =>& Array()); // reference array 

    while ($elements) {// repeat till $elements is empty 

     // loop through (remaining) elements 
     foreach ($elements as $id => $element) { 

      $pid = $element['parentId']; // shortcut 

      if (array_key_exists($pid,$r)) { // parent already parsed -> can add this element 

       // use parent's reference to elements array to add this element ($r[$pid] =& $e[path][to][$pid]) 
       $r[$pid] ['elements'][$id] = $element; 

       // create reference for this element using parent's reference 
       $r[$id] =& $r[$pid]['elements'][$id]; 

       // unset current element in input array in order to limit while-loop 
       unset($elements[$id]); 

      } 

     } 
    } 

return $e; // or whatever you need, the reference array can be useful if you need to fetch an element only knowing its id 

} 

Cosa lo fa:

  • dichiarare un array per i risultati ($e) e aggiungere un elemento radice (0/null)
  • dichiarare una matrice per i riferimenti ($r) e già riferimento $r[0] a $e[0]
  • scorrere l'array di input finché non è vuoto
    • fare per ogni elemento:
    • di controllo se genitore già è gestita, in caso affermativo, si sa dove questo elemento appartiene e può aggiungere
      (questo è quello che mi ha impedito di ottenerlo worki ng il primo paio di tentativi e perché è necessario il ciclo while)
    • aggiungere l'elemento corrente utilizzando di riferimento del genitore (notare il & dopo =!)
    • creare un nuovo riferimento utilizzando il riferimento del genitore
    • disinserire la corrente elemento dalla matrice di input
      (si può lasciare questo se vi piace infiniti while loop;))
  • di cource return quello che ti serve!

I vantaggi di questo metodo sono:

  • si riduce la quantità di foreach loop (si assegna il massimo possibile da ciò che si sa al momento)
  • ogni ciclo si accorcia, perché si elementi parsed unset della matrice di input ($elements)
  • è anche possibile restituire la matrice di riferimento ed essere in grado di ottenere rapidamente i parametri dell'elemento supponendo sai che è id

In altre parole: dovrebbe essere più veloce (non l'ho testato!), Almeno per compiti più complessi come quello per cui lo uso.

Problemi correlati