web-dev-qa-db-fra.com

créer un arbre de tableau à partir de la liste des tableaux

j'ai une liste comme celle-ci:

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 bien plus grand donc j'ai besoin d'un moyen efficace pour en faire une structure arborescente comme celle-ci:

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 gauche et droite dans ma base de données. des idées?

43
Thunderstriker

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;
}
55
Thunderstriker

petite correction si vous avez besoin de plus d'un élément parentid [0] :)

$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;
}
40
arthur

Encore une retouche de variante de Thunderstriker - 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);

Aire de jeux: https://www.tehplayground.com/5V8QSqnmFJ2wcIoj

12
Vasily

Voici mon adaptation de refonte d'Arthur :

/* 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);
9
Pierre de LESPINAY

J'ai créé une fonction de tri inhabituelle ("basée sur le temps" au lieu de récursive) mais multidimensionnelle qui parcourt le tableau jusqu'à ce qu'il n'y ait plus d'orphelins. Voici 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 de 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' ),
);

sage: la fonction a besoin de $ a (le tableau), $ parent_key (le nom de la colonne où l'identifiant du père est enregistré), $ children_key (le nom de la colonne où les enfants seront bouge toi). Il ne renvoie rien (le tableau est modifié par référence). Exemple:

treeze( $ARRAY, 'father', 'children' );
echo "<pre>"; print_r( $ARRAY );
6
Marco Panichi

Y a-t-il une raison pour laquelle cette méthode en trois passes ne fonctionnerait pas? Je n'ai fait aucun test pour comparer la vitesse à certaines des solutions récursives, mais cela semblait plus simple. Si votre tableau initial est déjà associatif avec les ID étant la clé, vous pouvez ignorer la première 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;
}
1
psyon

Une façon de le faire est d'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 renvoyé et en le remplissant dans le nouveau tableau enfant 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 ressembleront à quelque chose comme:

function recursiveChildren ($ items_array, $ parent_id = 0)

Essentiellement, il trouvera tous ceux dont le parent est 0, puis pour chacun d'entre eux, il trouvera tous ceux qui ont cet identifiant comme parent, et pour chacun d'eux .. ainsi de suite.

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

1
DampeS8N
//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>";
1
Pham