Başka, bir hiyerarşi içine $tree olarak düz bir yapıya dönüştürmek için daha basitleştirilmiş bir yoldur. Sadece tek bir geçici dizi maruz için gereklidir:
// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
$flat[$name]['name'] = $name; # self
if (NULL === $parent)
{
# no parent, is root element, assign it to $tree
$tree = &$flat[$name];
}
else
{
# has parent, add self as child
$flat[$parent]['children'][] = &$flat[$name];
}
}
unset($flat);
Bu çok boyutlu bir diziye hiyerarşi almak için hepsi:
Array
(
[children] => Array
(
[0] => Array
(
[children] => Array
(
[0] => Array
(
[name] => H
)
[1] => Array
(
[name] => F
)
)
[name] => G
)
[1] => Array
(
[name] => E
[children] => Array
(
[0] => Array
(
[name] => A
)
[1] => Array
(
[children] => Array
(
[0] => Array
(
[name] => B
)
)
[name] => C
)
)
)
)
[name] => D
)
Eğer Özyinelemeyi (büyük yapıları ile bir yük olabilir) kaçınmak istiyorsanız çıkışı daha az önemsiz değildir.
Ben her zaman bir dizi çıktısı için UL / LI "ikilemi" çözmek istiyordu. Ikilem her bir öğesi çocuklara takip ya da kaç önceki elemanlar kapalı olması gerekir olup olmadığını bilmiyor olmasıdır. Başka bir cevap ben zaten RecursiveIteratorIterator kullanarak ve getDepth() ve diğer meta-bilgi arıyorsanız benim kendi yazılı Iterator sağladığı o çözdü: {[(3) }] ancak gizleme altağaçlara . That answer de iteratörler ile oldukça esnek olduğunuzu gösterir "kapalı".
Bununla birlikte bu liste önceden kriteri olarak, bu nedenle, örneğin uygun olmaz. Ayrıca ben her zaman standart ağaç yapısı çeşit ve HTML'ın <ul> ve <li> öğeleri için bu çözmek istiyordu.
Ben geldi temel kavram aşağıdaki:
TreeNode - Abstracts each element into a simple TreeNode o değer sağlayabilir yazın (örn: Name) ve çocukları olup olmadığını.
TreeNodesIterator - A RecursiveIterator O bu TreeNodes kümesi (array) üzerinde yineleme yapabiliyor. O çocuğu var ve hangilerinin ise TreeNode zaten yazın bildiği gibi bu oldukça basittir.
RecursiveListIterator - A RecursiveIteratorIterator that has all the events needed when it recursively iterate over any kind of RecursiveIterator:
beginIteration / endIteration - başlayın ve ana listenin sonunda.
beginElement / endElement - başlayın ve her elemanın sonu.
beginChildren / endChildren - Begin and end of each children list.
This RecursiveListIterator only provides these events in a form of function calls. children lists, as it is typical for <ul><li> lists, are opened and closed inside it's parent <li> element. Therefore the endElement event is fired after the according endChildren event. This could be changed or made configurable to broaden the use this class. The events are distributed as function calls to a decorator object then, to keep things apart.
ListDecorator - A "decorator" class that is just a receiver of the events of RecursiveListIterator.
Ben ana çıkış mantığı ile başlar. Şimdi hiyerarşik $tree dizi Taken, son kodu aşağıdaki gibi görünür:
$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
foreach($rit as $item)
{
$inset = $decor->inset(1);
printf("%s%s\n", $inset, $item->getName());
}
Önce ListDecorator sadece <ul> ve <li> elemanları ve liste yapısı çıkış nasıl karar vermektir sarar içine bakalım:
class ListDecorator
{
private $iterator;
public function __construct(RecursiveListIterator $iterator)
{
$this->iterator = $iterator;
}
public function inset($add = 0)
{
return str_repeat(' ', $this->iterator->getDepth()*2+$add);
}
Yapıcı bu üzerinde çalışıyor liste yineleyici alır. inset çıkışının güzel girinti için sadece bir yardımcı fonksiyonudur. Gerisi, her olay için sadece çıkış işlevleri şunlardır:
public function beginElement()
{
printf("%s<li>\n", $this->inset());
}
public function endElement()
{
printf("%s</li>\n", $this->inset());
}
public function beginChildren()
{
printf("%s<ul>\n", $this->inset(-1));
}
public function endChildren()
{
printf("%s</ul>\n", $this->inset(-1));
}
public function beginIteration()
{
printf("%s<ul>\n", $this->inset());
}
public function endIteration()
{
printf("%s</ul>\n", $this->inset());
}
}
Akılda bu çıkış fonksiyonları ile, bu yine ana çıkış wrap-up / döngü, ben adım adım geçmesi:
$root = new TreeNode($tree);
: Kök TreeNode üzerine yineleme başlatmak için kullanılacak oluşturun
$it = new TreeNodesIterator(array($root));
Bu TreeNodesIterator a RecursiveIterator tek $root düğüm üzerinde yinelemeli yineleme kılmasıdır. Bu sınıf üzerinde yineleme için bir şey gerekiyor ve aynı zamanda TreeNode elemanlarının bir dizi olan çocukların bir dizi ile yeniden kullanımını sağlar çünkü bir dizi olarak geçirilir.
$rit = new RecursiveListIterator($it);
Bu RecursiveListIterator a RecursiveIteratorIterator bahsedilen etkinlik sağlamasıdır. O faydalanmak için, sadece bir ListDecorator (yukarıda sınıfı) sağlanan ve tahsis gerekiyor addDecorator:
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
Sonra her şey bir set-up için sadece foreach her düğüm üzerinde ve çıkış:
foreach($rit as $item)
{
$inset = $decor->inset(1);
printf("%s%s\n", $inset, $item->getName());
}
Bu örnekte gösterildiği gibi, bütün çıkış lojik ListDecorator sınıfı ve bu tek foreach içinde kapsüllenir. Bütün özyinelemeli geçişi tamamen o içten Yinelemesiz fonksiyon çağrıları yapılır anlamına gelir, bir yığılmış prosedür sağlanan SPL özyinelemeli Yineleyicilerin kapsüllü olmuştur.
Tabanlı olay ListDecorator size özel çıktı değiştirmek için ve aynı veri yapısı için listeleri birden fazla tip sağlamak için sağlar. Dizi veri TreeNode içine kapsüllü olduğu gibi bu girişi değiştirmek bile mümkün.
Tam kod örneği:
<?php
namespace My;
$tree = array('H' => 'G', 'F' => 'G', 'G' => 'D', 'E' => 'D', 'A' => 'E', 'B' => 'C', 'C' => 'E', 'D' => null);
// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
$flat[$name]['name'] = $name; # self
if (NULL === $parent)
{
# no parent, is root element, assign it to $tree
$tree = &$flat[$name];
}
else
{
# has parent, add self as child
$flat[$parent]['children'][] = &$flat[$name];
}
}
unset($flat);
class TreeNode
{
protected $data;
public function __construct(array $element)
{
if (!isset($element['name']))
throw new InvalidArgumentException('Element has no name.');
if (isset($element['children']) && !is_array($element['children']))
throw new InvalidArgumentException('Element has invalid children.');
$this->data = $element;
}
public function getName()
{
return $this->data['name'];
}
public function hasChildren()
{
return isset($this->data['children']) && count($this->data['children']);
}
/**
* @return array of child TreeNode elements
*/
public function getChildren()
{
$children = $this->hasChildren() ? $this->data['children'] : array();
$class = get_called_class();
foreach($children as &$element)
{
$element = new $class($element);
}
unset($element);
return $children;
}
}
class TreeNodesIterator implements \RecursiveIterator
{
private $nodes;
public function __construct(array $nodes)
{
$this->nodes = new \ArrayIterator($nodes);
}
public function getInnerIterator()
{
return $this->nodes;
}
public function getChildren()
{
return new TreeNodesIterator($this->nodes->current()->getChildren());
}
public function hasChildren()
{
return $this->nodes->current()->hasChildren();
}
public function rewind()
{
$this->nodes->rewind();
}
public function valid()
{
return $this->nodes->valid();
}
public function current()
{
return $this->nodes->current();
}
public function key()
{
return $this->nodes->key();
}
public function next()
{
return $this->nodes->next();
}
}
class RecursiveListIterator extends \RecursiveIteratorIterator
{
private $elements;
/**
* @var ListDecorator
*/
private $decorator;
public function addDecorator(ListDecorator $decorator)
{
$this->decorator = $decorator;
}
public function __construct($iterator, $mode = \RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
{
parent::__construct($iterator, $mode, $flags);
}
private function event($name)
{
// event debug code: printf("--- %'.-20s --- (Depth: %d, Element: %d)\n", $name, $this->getDepth(), @$this->elements[$this->getDepth()]);
$callback = array($this->decorator, $name);
is_callable($callback) && call_user_func($callback);
}
public function beginElement()
{
$this->event('beginElement');
}
public function beginChildren()
{
$this->event('beginChildren');
}
public function endChildren()
{
$this->testEndElement();
$this->event('endChildren');
}
private function testEndElement($depthOffset = 0)
{
$depth = $this->getDepth() + $depthOffset;
isset($this->elements[$depth]) || $this->elements[$depth] = 0;
$this->elements[$depth] && $this->event('endElement');
}
public function nextElement()
{
$this->testEndElement();
$this->event('{nextElement}');
$this->event('beginElement');
$this->elements[$this->getDepth()] = 1;
}
public function beginIteration()
{
$this->event('beginIteration');
}
public function endIteration()
{
$this->testEndElement();
$this->event('endIteration');
}
}
class ListDecorator
{
private $iterator;
public function __construct(RecursiveListIterator $iterator)
{
$this->iterator = $iterator;
}
public function inset($add = 0)
{
return str_repeat(' ', $this->iterator->getDepth()*2+$add);
}
public function beginElement()
{
printf("%s<li>\n", $this->inset(1));
}
public function endElement()
{
printf("%s</li>\n", $this->inset(1));
}
public function beginChildren()
{
printf("%s<ul>\n", $this->inset());
}
public function endChildren()
{
printf("%s</ul>\n", $this->inset());
}
public function beginIteration()
{
printf("%s<ul>\n", $this->inset());
}
public function endIteration()
{
printf("%s</ul>\n", $this->inset());
}
}
$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
foreach($rit as $item)
{
$inset = $decor->inset(2);
printf("%s%s\n", $inset, $item->getName());
}
Outpupt:
<ul>
<li>
D
<ul>
<li>
G
<ul>
<li>
H
</li>
<li>
F
</li>
</ul>
</li>
<li>
E
<ul>
</li>
<li>
A
</li>
<li>
C
<ul>
<li>
B
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Demo (PHP 5.2 variant)
Olası bir varyantı herhangi bir RecursiveIterator üzerinde dolaşır ve oluşabilecek tüm olayların üzerinde bir yineleme sağlar bir yineleyici olacaktır. Foreach döngüsü içinde bir switch / case sonra olaylar ile anlaşma olabilir.
İlgili: