2010-11-16 13 views
37

Ich habe eine Liste wie folgt aus:erstellen Array Baum von Array-Liste

array(
    array(id=>100, parentid=>0, name=>'a'), 
    array(id=>101, parentid=>100, name=>'a'), 
    array(id=>102, parentid=>101, name=>'a'), 
    array(id=>103, parentid=>101, name=>'a'), 
) 

aber viel größer, so dass ich eine effiziente Art und Weise müssen diese in einem Baum wie Struktur wie folgt zu machen:

array(
    id=>100, parentid=>0, name=>'a', children=>array(
    id=>101, parentid=>100, name=>'a', children=>array(
     id=>102, parentid=>101, name=>'a', 
     id=>103, parentid=>101, name=>'a', 
    ) 
) 
) 

Ich kann keine Dinge wie verschachtelte Mengen oder Dinge wie diese verwenden, da ich Links und Rechts Werte in meiner Datenbank hinzufügen kann. irgendwelche Ideen?

+0

nicht bekommen es ... Ihre Liste ist ein PHP-Array? – acm

+1

mögliche Duplikate von [Wie kann ich eine Reihe von Eltern-Kind-Beziehungen in einen hierarchischen Baum umwandeln?] (Http://stackoverflow.com/questions/2915748/how-can-i-convert-a-series-of-parent - Kinderbeziehungen - in eine hierarchische Struktur) – GWW

+0

@andre OP sucht nach einer Adjazenzliste. Dafür gibt es eine Reihe von Duplikaten. – Gordon

Antwort

45

oke das ist, wie ich es gelöst:

$arr = array(
    array('id'=>100, 'parentid'=>0, 'name'=>'a'), 
    array('id'=>101, 'parentid'=>100, 'name'=>'a'), 
    array('id'=>102, 'parentid'=>101, 'name'=>'a'), 
    array('id'=>103, 'parentid'=>101, 'name'=>'a'), 
); 

$new = array(); 
foreach ($arr as $a){ 
    $new[$a['parentid']][] = $a; 
} 
$tree = createTree($new, array($arr[0])); 
print_r($tree); 

function createTree(&$list, $parent){ 
    $tree = array(); 
    foreach ($parent as $k=>$l){ 
     if(isset($list[$l['id']])){ 
      $l['children'] = createTree($list, $list[$l['id']]); 
     } 
     $tree[] = $l; 
    } 
    return $tree; 
} 
+1

Dies funktioniert gut, nur stellen Sie sicher, dass, wenn Sie mehr als ein Element mit parentid = 0, durch alle Elemente durchlaufen und überprüfen Sie für parentid == 0. Wenn das wahr ist Führen Sie createTree für dieses Element aus und hängen Sie es an Ihr Baumarray an. Ansonsten funktioniert diese Routine nur für den ersten Gegenstand, wo parentid = 0 – Adam

+0

Genau das, was ich jetzt brauchte, danke, danke –

+0

Found this solusion, half sehr! – Altenrion

1

Eine Möglichkeit, dies zu tun, ist mit einer rekursiven Funktion, die zuerst alle unteren Werte der Liste findet und sie zu einem neuen Array hinzufügt. Dann verwenden Sie für jede neue ID dieselbe Funktion für diese ID, indem Sie das zurückgegebene Array verwenden und es in das neue untergeordnete Array dieses Elements stopfen. Schließlich geben Sie Ihr neues Array zurück.

Ich werde nicht für Sie die ganze Arbeit tun, aber die Parameter der Funktion in etwa so aussehen:

Funktion recursiveChildren ($ items_array, $ parent_id = 0)

Im Wesentlichen findet es all diejenigen mit Eltern von 0, dann für jeden von denen wird es alle mit dieser ID als Eltern finden, und für jeden von denen .. so weiter.

Das Endergebnis sollte sein, was Sie suchen.

+0

Das Problem mit diesem Algorithmus ist, dass es wahrscheinlich ist O (n^2). Betrachten Sie ein Array, bei dem jedes Element das übergeordnete Element des nächsten ist. Dieser Algorithmus würde das Array n Mal scannen, was zu n (n + 1)/2 Operationen führt. – erisco

+0

So entfernen Sie die Elemente aus dem alten Array, wie Sie sie vor dem Weiterleiten finden. Meine Absicht hier war nur, um eine Skizze einer Funktion zu bekommen, die die Arbeit machen wird. Mach den Job nicht schnell. Das ist zur späteren Optimierung. Dies ist das Web. Caching ist ein besserer Ort, um diese Art von mentalen Energien auszugeben. – DampeS8N

+0

Meine Berechnung von n (n + 1)/2 Operationen erklärt die Tatsache, dass das Array nach jedem Scan schrumpft. Das OP erklärte, dass seine Datenstruktur "viel größer" sei; Ich denke, dass O (n^2) zu teuer ist. – erisco

38

kleine fix, wenn Sie mehr als 1 parentid [0] Element brauchen :)

$arr = array(
    array('id'=>100, 'parentid'=>0, 'name'=>'a'), 
    array('id'=>101, 'parentid'=>100, 'name'=>'a'), 
    array('id'=>102, 'parentid'=>101, 'name'=>'a'), 
    array('id'=>103, 'parentid'=>101, 'name'=>'a'), 
); 

$new = array(); 
foreach ($arr as $a){ 
    $new[$a['parentid']][] = $a; 
} 
$tree = createTree($new, $new[0]); // changed 
print_r($tree); 

function createTree(&$list, $parent){ 
    $tree = array(); 
    foreach ($parent as $k=>$l){ 
     if(isset($list[$l['id']])){ 
      $l['children'] = createTree($list, $list[$l['id']]); 
     } 
     $tree[] = $l; 
    } 
    return $tree; 
} 
+0

Irgendwelche Ideen, wie man dieses mit irgendeinem parentId auf dem niedrigsten Niveau arbeitet? http://stackoverflow.com/questions/11942115/convert-flat-array-to-nested-parent-child-structure#comment15908756_11942115 –

+1

Was ist, wenn wir hier eine Schleife haben? –

6

ich eine ungewöhnliche erstellt ('während Basis 'statt rekursiv) aber multidimensional Sortierfunktion, die das Array laufen, bis es keine Waisen gibt. Hier ist die Funktion:

function treeze(&$a, $parent_key, $children_key) 
{ 
    $orphans = true; $i; 
    while($orphans) 
    { 
     $orphans = false; 
     foreach($a as $k=>$v) 
     { 
      // is there $a[$k] sons? 
      $sons = false; 
      foreach($a as $x=>$y) 
      if(isset($y[$parent_key]) and $y[$parent_key]!=false and $y[$parent_key]==$k) 
      { 
       $sons=true; 
       $orphans=true; 
       break; 
      } 

      // $a[$k] is a son, without children, so i can move it 
      if(!$sons and isset($v[$parent_key]) and $v[$parent_key]!=false) 
      { 
       $a[$v[$parent_key]][$children_key][$k] = $v; 
       unset($a[$k]); 
      } 
     } 
    } 
} 

Empfehlung: der Schlüssel jedes Element des Arrays hat, die ID fo das Element selbst. Beispiel:

$ARRAY = array(
    1 => array('label' => "A"), 
    2 => array('label' => "B"), 
    3 => array('label' => "C"), 
    4 => array('label' => "D"), 
    5 => array('label' => "one", 'father' => '1'), 
    6 => array('label' => "two", 'father' => '1'), 
    7 => array('label' => "three", 'father' => '1'), 
    8 => array('label' => "node 1", 'father' => '2'), 
    9 => array('label' => "node 2", 'father' => '2'), 
    10 => array('label' => "node 3", 'father' => '2'), 
    11 => array('label' => "I", 'father' => '9'), 
    12 => array('label' => "II", 'father' => '9'), 
    13 => array('label' => "III", 'father' => '9'), 
    14 => array('label' => "IV", 'father' => '9'), 
    15 => array('label' => "V", 'father' => '9'), 
); 

Verbrauch: die Funktion braucht ein (das Array), $ parent_key $ (den Namen der Spalte, in der die ID des Vaters gerettet wird), $ children_key (den Namen der Spalte, in der die Kinder werden sich bewegen). Es gibt nichts zurück (das Array wird durch Referenz geändert). Beispiel:

treeze($ARRAY, 'father', 'children'); 
echo "<pre>"; print_r($ARRAY); 
+1

Guter Weg, schneller als Wiederholung! Benötigt ein kleines Update, "Hinweis: Undefinierter Index: Vater" –

+0

@PeterKrauss Ich habe den Hinweis behoben (und auch ein wenig die Formatierung zu verbessern) –

7

Hier ist meine Anpassung von arthur's rework:

/* Recursive branch extrusion */ 
function createBranch(&$parents, $children) { 
    $tree = array(); 
    foreach ($children as $child) { 
     if (isset($parents[$child['id']])) { 
      $child['children'] = 
       $this->createBranch($parents, $parents[$child['id']]); 
     } 
     $tree[] = $child; 
    } 
    return $tree; 
} 

/* Initialization */ 
function createTree($flat, $root = 0) { 
    $parents = array(); 
    foreach ($flat as $a) { 
     $parents[$a['parent']][] = $a; 
    } 
    return $this->createBranch($parents, $parents[$root]); 
} 

Verwendung:

$tree = createTree($flat); 
0

Gibt es einen Grund, warum diese drei Pass-Methode nicht funktionieren würde? Ich habe keine Tests durchgeführt, um die Geschwindigkeit mit einigen der rekursiven Lösungen zu vergleichen, aber es schien mir direkter zu sein. Wenn Ihr initiales Array bereits assoziativ mit den IDs ist, die der Schlüssel sind, können Sie das erste foreach() überspringen.

function array_tree(&$array) { 
    $tree = array(); 

    // Create an associative array with each key being the ID of the item 
    foreach($array as $k => &$v) $tree[$v['id']] = &$v; 

    // Loop over the array and add each child to their parent 
    foreach($tree as $k => &$v) { 
     if(!$v['parent']) continue; 
     $tree[$v['parent']]['children'][] = &$v; 
    } 

    // Loop over the array again and remove any items that don't have a parent of 0; 
    foreach($tree as $k => &$v) { 
     if(!$v['parent']) continue; 
     unset($tree[$k]); 
    } 

    return $tree; 
} 
9

Eine weitere Nacharbeit Thunderstriker's variant - die gesamte Logik in einer Funktion:

function buildTree($flat, $pidKey, $idKey = null) 
{ 
    $grouped = array(); 
    foreach ($flat as $sub){ 
     $grouped[$sub[$pidKey]][] = $sub; 
    } 

    $fnBuilder = function($siblings) use (&$fnBuilder, $grouped, $idKey) { 
     foreach ($siblings as $k => $sibling) { 
      $id = $sibling[$idKey]; 
      if(isset($grouped[$id])) { 
       $sibling['children'] = $fnBuilder($grouped[$id]); 
      } 
      $siblings[$k] = $sibling; 
     } 

     return $siblings; 
    }; 

    $tree = $fnBuilder($grouped[0]); 

    return $tree; 
} 

// Example: 
$flat = [ 
    ['id'=>100, 'parentID'=>0, 'name'=>'a'], 
    ['id'=>101, 'parentID'=>100, 'name'=>'a'], 
    ['id'=>102, 'parentID'=>101, 'name'=>'a'], 
    ['id'=>103, 'parentID'=>101, 'name'=>'a'], 
]; 

$tree = buildTree($flat, 'parentID', 'id'); 
print_r($tree); 

Spielplatz: https://www.tehplayground.com/5V8QSqnmFJ2wcIoj

+3

Ich mochte dieses Beispiel, also wickelte ich es in eine Klasse und machte es auf GitHub verfügbar Hier; https://github.com/srayner/NavTree – srayner

+0

Wie fügt man diesem Baum ein Kind als Kind eines bestimmten Elternteils hinzu? –

+0

@HappyCoder fügen Sie einfach ein Element zur $ flat hinzu, zum Beispiel '['id' => 103, 'parentID' => 101, 'name' => 'a']' - es ist ein Kind einer '['id '=> 101,' parentID '=> 100,' name '=>' a '] 'Element – Vasily

1

//if order by parentid, id 
$arr = array(
    array('id'=>100, 'parentid'=>0, 'name'=>'a'), 
    array('id'=>101, 'parentid'=>100, 'name'=>'a'), 
    array('id'=>102, 'parentid'=>101, 'name'=>'a'), 
    array('id'=>103, 'parentid'=>101, 'name'=>'a'), 
); 

$arr_tree = array(); 
$arr_tmp = array(); 

foreach ($arr as $item) { 
    $parentid = $item['parentid']; 
    $id = $item['id']; 

    if ($parentid == 0) 
    { 
     $arr_tree[$id] = $item; 
     $arr_tmp[$id] = &$arr_tree[$id]; 
    } 
    else 
    { 
     if (!empty($arr_tmp[$parentid])) 
     { 
      $arr_tmp[$parentid]['children'][$id] = $item; 
      $arr_tmp[$id] = &$arr_tmp[$parentid]['children'][$id]; 
     } 
    } 
} 

unset($arr_tmp); 
echo '<pre>'; print_r($arr_tree); echo "</pre>";