2010-12-01 51 views
6

J'ai un arbre de catégories de la structure suivante:Recursion et passage par référence

[6] => Array 
    (
     [id] => 6 
     [name] => computers 
     [productCount] => 0 
     [children] => Array 
      (
       [91] => Array 
        (
         [id] => 91 
         [name] => notebook 
         [productCount] => 5 
         [children] => Array 
          (
          ) 
        ) 

       [86] => Array 
        (
         [id] => 86 
         [name] => desktop 
         [productCount] => 0 
         [children] => Array 
          (
          ) 
        ) 
      ) 
    ) 

A côté d'une sous-catégorie, chaque catégorie peut contenir des produits (comme un dossier peut contenir des sous-dossiers et fichiers seulement). J'essaye d'écrire une fonction récursive dont je veux prendre cette rangée comme référence et dépouiller les deux catégories de feuilles avec [productCount] = 0 et toutes les catégories parent qui contiennent de tels nœuds vides. En d'autres termes, après traitement, je veux avoir seulement les catégories qui contiennent des produits sur n'importe quel sous-niveau.

J'ai écrit du code, maintenant le débogage et il ne dépouille pas les nœuds vides. Peut-être que je n'utilise pas correctement les références. S'il vous plaît, aidez-moi à le réparer, si possible.

function pruneTree(& $node) { 
    if (! $node['children'] && ! $node['productCount']) { 
     unset($node); 
    } 
    if (! empty($node['children'])) { 
     foreach ($node['children'] as $key => $child) { 
      pruneTree($node['children'][$key]); 
     } 
    } 
    return; 
} 
+0

Est-ce que 'array() == false'? – jantimon

+1

@Ghommey: Oui, en PHP un tableau vide est considéré comme falsifié. – BoltClock

Répondre

4

Vous pouvez également modifier le paramètre dans la fonction de prendre un ensemble de noeuds au lieu d'un seul nœud. Cela change la récursion légèrement, et empêche la nécessité de transmettre une clé:

function pruneTree(&$nodes) { 
    foreach ($nodes as $key => $node) { 
     if (!$node['children'] && !$node['productCount']) { 
      unset($nodes[$key]); 
     } elseif (!empty($node['children'])) { 
      pruneTree($nodes[$key]['children']); 
      // This line checks if all the children have been pruned away: 
      if (empty($nodes[$key]['children'])) { 
       unset($nodes[$key]); 
      } 
     } 
    } 
} 

En outre, a ajouté un chèque qui garantit que si tous les nœuds enfants sont élagués, le nœud parent (maintenant, feuille) obtient également élagué.

Espérons que cela aide!


données de test:

$data = array(
    6 => array(
     'id' => 6, 
     'name' => 'computers', 
     'productCount' => 0, 
     'children' => array(
      91 => array(
       'id' => 91, 
       'name' => 'notebook', 
       'productCount' => 5, 
       'children' => array() 
      ), 
      86 => array(
       'id' => 86, 
       'name' => 'desktop', 
       'productCount' => 0, 
       'children' => array() 
      ) 
     ) 
    ) 
); 

L'appel:

pruneTree($data); 
echo '<pre>'; 
print_r($data); 
echo '</pre>'; 
+0

Il s'avère qu'il est impossible d'utiliser 'unset ($ nodes [$ key]);' à l'intérieur d'une fonction pour modifier le tableau initial qui est passé par référence, car il désinstallera simplement la variable de référence dans la portée des fonctions. – sevenWonders

+0

@sevenWonders - J'ai oublié de mentionner que j'ai testé ce script (ainsi que celui de Gumbo) et qu'ils fonctionnent tous les deux. Il n'y a pratiquement aucune différence, sauf que je trouve la clé dans la fonction appelée avant de la désactiver. – RabidFire

+0

@RabidFire - Bizarrement quand je teste votre fonction j'obtiens une erreur "Seules les variables peuvent être passées par référence" à la ligne 'pruneTree ($ nodes [$ key] ['children']);'. – sevenWonders

5

unset supprime uniquement la référence, mais pas la variable référencée:

Si une variable qui est passée par référence est unset() à l'intérieur d'une fonction, seule la variable locale est détruite. La variable dans l'environnement appelant conservera la même valeur qu'avant unset() a été appelée.

Vous devez donc passer le tableau parent et la clé pour supprimer cette variable:

function pruneTree(&$parent, $key) { 
    $node = &$parent[$key]; 
    if (!$node['children'] && !$node['productCount']) { 
     unset($parent[$key]); 
    } 
    if (!empty($node['children'])) { 
     foreach ($node['children'] as $key => &$child) { 
      pruneTree($node['children'], $key); 
     } 
    } 
} 
+0

Merci, Gumbo! J'ai cherché des indices sur la page "références" du manuel, et j'ai raté le point avec "unset" et la portée. – sevenWonders

0

Je ne sais pas si tel est le cas, mais quand je avais besoin de changer les valeurs récursive dans le tableau , j'avais besoin de passer & à la valeur de foreach aussi bien.

private function convertXMLPart(&$array) { 
     foreach ($array as $rowKey => &$row) { 
      if (gettype($row) != 'string') { 
       $row = (array)$row; 
       if (!empty($row['@attributes'])) { 
        foreach ($row['@attributes'] as $key => $value) { 
         $row[$key] = $value; 
        } 
        unset($row['@attributes']); 
        $array[$rowKey] = $row; 
       } 
       $this->convertXMLPart($row); 
      } 
     } 
    }