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?
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;
}
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;
}
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
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);
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 );
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;
}
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.
//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>";