2010-11-16 4 views
37

i ont une liste comme ceci:créer arbre de tableau de la liste de tableau

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'), 
) 

mais beaucoup plus donc je besoin d'un moyen efficace de faire cela dans un arbre comme la structure comme ceci:

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', 
    ) 
) 
) 

Je ne peux pas utiliser des choses comme un ensemble imbriqué ou des choses comme ça parce que je peux ajouter des valeurs de gauche et de droite dans ma base de données. des idées?

+0

ne pas le faire ... votre liste est un tableau PHP? – acm

+1

duplication possible de [Comment puis-je convertir une série de relations parent-enfant en un arbre hiérarchique?] (Http://stackoverflow.com/questions/2915748/how-can-i-convert-a-series-of-parent -child-relations-into-a-hierarchical-tree) – GWW

+0

@andre OP recherche une liste d'adjacence. Il y a un certain nombre de doublons pour cela. – Gordon

Répondre

45

Oke voici comment je l'ai résolu:

$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

Cela fonctionne bien, assurez-vous que, si vous avez plus d'un élément avec parentid = 0, faire une boucle sur tous les éléments et vérifier parentid == 0. Si c'est vrai , puis exécutez createTree sur cet élément et l'ajouter à votre tableau d'arbres. Sinon, cette routine ne fonctionne que pour le premier élément où parentid = 0 – Adam

+0

Exactement ce dont j'avais besoin maintenant merci –

+0

Trouvé cette solusion, beaucoup aidé! – Altenrion

1

Une façon de procéder consiste à utiliser une fonction récursive qui trouve d'abord toutes les valeurs inférieures de la liste, en les ajoutant à un nouveau tableau. Ensuite, pour chaque nouvel identifiant, vous utilisez la même fonction sur cet identifiant, en prenant le tableau retourné et en l'insérant dans le nouveau tableau children de cet élément. Enfin, vous retournez votre nouveau tableau.

Je ne ferai pas tout le travail pour vous, mais les paramètres de la fonction ressemblera à quelque chose comme:

recursiveChildren fonction (items_array $, $ parent_id = 0)

Essentiellement, il va trouver tous ceux avec un parent de 0, alors pour chacun de ceux-là, ils trouveront tous ceux qui ont cet id comme parent, et pour chacun d'entre eux ... ainsi de suite.

Le résultat final devrait être ce que vous cherchez.

+0

Le problème avec cet algorithme est qu'il est probable O (n^2). Considérons un tableau où chaque élément est le parent du suivant. Cet algorithme balayerait le tableau n fois, ce qui donnerait n (n + 1)/2 opérations. – erisco

+0

Supprimez donc les éléments de l'ancien tableau tels que vous les avez trouvés avant de les transmettre. Mon intention ici était juste d'obtenir une esquisse d'une fonction qui fera le travail. Ne pas faire le travail rapidement. C'est pour l'optimisation plus tard. C'est le web. La mise en cache est un meilleur endroit pour dépenser ces sortes d'énergies mentales. – DampeS8N

+0

Mon calcul des opérations n (n + 1)/2 expliquait le fait que le tableau rétrécit après chaque balayage. Le PO a déclaré que sa structure de données était "beaucoup plus grande"; Je pense qu'il est implicite que O (n^2) est trop cher. – erisco

38

petite solution si vous avez besoin de plus de 1 parentid [0] :) élément

$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

Des idées comment faire fonctionner celui-ci avec n'importe quel parentId au niveau le plus bas? http://stackoverflow.com/questions/11942115/convert-flat-array-to-nested-parent-child-structure#comment15908756_11942115 –

+1

Et si nous aurions une boucle ici? –

6

J'ai créé un inhabituel («tout-basé 'au lieu de récursif) mais multidimensionnelle fonction de tri qui marchent le tableau jusqu'à ce qu'il n'y ait pas d'orphelins. Ici, la fonction:

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

Recommandation: la clé de chaque élément du tableau doit être l'id fi l'élément lui-même. Exemple:

$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'), 
); 

Utilisation: la fonction doit $ a (le tableau), Parent_Key de $ (le nom de la colonne où l'identification du père est enregistré), children_key de $ (le nom de la colonne où les enfants seront déménagés). Il ne renvoie rien (le tableau est changé par référence). Exemple:

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

Bonne façon, plus rapide que la récurrence! Besoin d'un petit correctif, "Note: Indéfini index: père" –

+0

@PeterKrauss J'ai corrigé l'avis (et aussi améliorer un peu la mise en forme) –

7

Voici mon adaptation de 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]); 
} 

Utilisation:

$tree = createTree($flat); 
0

Y at-il raison de cette méthode trois passe ne fonctionnerait pas? Je n'ai fait aucun test pour comparer la rapidité à certaines des solutions récursives, mais il semblait plus à l'avant. Si votre tableau initial est déjà associatif avec les ID étant la clé, vous pouvez ignorer le premier foreach().

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

Encore une reprise de Thunderstriker's variant - toute la logique dans une fonction:

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

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

+3

J'ai aimé cet exemple, donc je l'ai enveloppé dans une classe et l'a rendu disponible sur github ici; https://github.com/srayner/NavTree – srayner

+0

Comment ajouter un élément à cet arbre en tant qu'enfant d'un parent particulier? –

+0

@HappyCoder ajoute simplement un élément au $ flat, par exemple '['id' => 103, 'parentID' => 101, 'name' => 'a']' - c'est un enfant d'un [[id '=> 101,' parentID '=> 100,' name '=>' a '] 'élément – 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>";