2010-02-05 5 views
2

Supposons que vous avez le tableau suivant:tableau Multidimensional itération

$nodes = array(
    "parent node", 
    "parent node", 
    array(
     "child node", 
     "child node", 
     array(
      "grand child node", 
      "grand child node"))); 

Comment feriez-vous transformer en une chaîne XML afin qu'il ressemble à:

<node> 
    <node>parent node</node> 
    <node>parent node</node> 
    <node> 
     <node>child node</node> 
     <node>child node</node> 
     <node> 
      <node>grand child node</node> 
      <node>grand child node</node> 
     </node> 
    </node> 
</node> 

Une façon de le faire serait être par une méthode récursive comme:

function traverse($nodes) 
{ 
    echo "<node>"; 

    foreach($nodes as $node) 
    { 
     if(is_array($node)) 
     { 
      traverse($node); 
     } 
     else 
     { 
      echo "<node>$node</node>"; 
     } 
    } 

    echo "</node>"; 
} 

traverse($nodes); 

Je cherche une approche qui utilise l'itération, bien que.

+0

pourquoi cherchez-vous une approche basée sur l'itération? Est-ce un devoir? – SilentGhost

+0

pourquoi? pourquoi ferais-tu ça? –

+0

Votre exemple utilise déjà l'itération en combinaison avec la récursivité. – meagar

Répondre

2
<?php 

$nodes = array(
    "parent node", 
    "parent node", 
    array(
     "child node", 
     "child node", 
     array(
      "grand child node", 
      "grand child node" 
     ) 
    ) 
); 

$s = '<node>'; 
$arr = $nodes; 

while(count($arr) > 0) 
{ 
    $n = array_shift($arr); 
    if(is_array($n)) 
    { 
     array_unshift($arr, null); 
     $arr = array_merge($n, $arr); 
     $s .= '<node>'; 
    } 
    elseif(is_null($n)) 
     $s .= '</node>'; 
    else 
     $s .= '<node>'.$n.'</node>'; 
} 
$s .= '</node>'; 

echo $s; 

?> 
+0

Brilliant! Je me demande si c'est plus rapide que l'approche basée sur la récursivité. –

15

Vous pouvez utiliser un Iterator itérer sur le tableau, puis produire votre sortie désirée:

class TranformArrayIterator extends RecursiveIteratorIterator 
{ 
    protected function indent() 
    { 
     echo str_repeat("\t", $this->getDepth()); 
     return $this; 
    } 
    public function beginIteration() 
    { 
     echo '<nodes>', PHP_EOL; 
    } 
    public function endIteration() 
    { 
     echo '</nodes>', PHP_EOL; 
    } 
    public function beginChildren() 
    { 
     $this->indent()->beginIteration(); 
    } 
    public function endChildren() 
    { 
     $this->indent()->endIteration(); 
    } 
    public function current() 
    { 
     return sprintf('%s<node>%s</node>%s', 
         str_repeat("\t", $this->getDepth() +1), 
         parent::current(), 
         PHP_EOL); 
    } 
} 

puis assembler comme ceci:

$iterator = new TranformArrayIterator(new RecursiveArrayIterator($nodes)); 

foreach($iterator as $val) { 
    echo $val; 
} 

sorties

<nodes> 
     <node>parent node</node> 
     <node>parent node</node> 
     <nodes> 
       <node>child node</node> 
       <node>child node</node> 
       <nodes> 
         <node>grand child node</node> 
         <node>grand child node</node> 
       </nodes> 
     </nodes> 
</nodes> 

Pour masquer $key lors de l'utilisation $key => $val, ajoutez à TraverseArrayIterator

public function key() 
{ 
    return ''; 
} 

Étant donné que votre objectif semble être de produire XML, vous pouvez aussi passer un XMLWriter en tant que collaborateur du Iterator. Cela permet plus de contrôle sur le XML généré et permet également que la sortie est XML valide:

class TranformArrayIterator extends RecursiveIteratorIterator 
{ 
    private $xmlWriter; 

    public function __construct(
     XmlWriter $xmlWriter, 
     Traversable $iterator, 
     $mode = RecursiveIteratorIterator::LEAVES_ONLY , 
     $flags = 0) 
    { 
     $this->xmlWriter = $xmlWriter; 
     parent::__construct($iterator, $mode, $flags); 
    } 

    public function beginIteration() 
    { 
     $this->xmlWriter->startDocument('1.0', 'utf-8'); 
     $this->beginChildren(); 
    } 
    public function endIteration() 
    { 
     $this->xmlWriter->endDocument(); 
    } 
    public function beginChildren() 
    { 
     $this->xmlWriter->startElement('nodes'); 
    } 
    public function endChildren() 
    { 
     $this->xmlWriter->endElement(); 
    } 
    public function current() 
    { 
     $this->xmlWriter->writeElement('node', parent::current()); 
    } 
} 

Vous aurait alors l'utiliser comme ceci:

$xmlWriter = new XmlWriter; 
$xmlWriter->openUri('php://output'); 
$xmlWriter->setIndent(true); 
$xmlWriter->setIndentString("\t"); 
$iterator = new TranformArrayIterator(
    $xmlWriter, 
    new RecursiveArrayIterator($nodes) 
); 

et foreach « ing dessus produira la même sortie alors (mais en ajoutant le prologue XML)