फ्लैट संरचना को $tree
पदानुक्रम में बदलने के लिए एक और सरल तरीका है । इसे उजागर करने के लिए केवल एक अस्थायी सरणी की आवश्यकता है:
// 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);
यह सब एक बहुआयामी सरणी में पदानुक्रम प्राप्त करने के लिए है:
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
)
यदि आप पुनरावृत्ति से बचना चाहते हैं तो आउटपुट कम तुच्छ है (बड़ी संरचनाओं के साथ बोझ हो सकता है)।
मैं हमेशा सरणी के आउटपुट के लिए UL / LI "दुविधा" को हल करना चाहता था। दुविधा यह है, कि प्रत्येक आइटम को यह नहीं पता है कि बच्चे अनुवर्ती होंगे या नहीं और कितने पूर्ववर्ती तत्वों को बंद करने की आवश्यकता है। एक अन्य उत्तर में मैंने पहले ही हल कर दिया है कि एक RecursiveIteratorIterator
और का उपयोग करके getDepth()
और अन्य मेटा-जानकारी है कि मेरे खुद के लिखित Iterator
प्रदान की: नेस्टेड सेट मॉडल एक <ul>
"छुपा" उपप्रकारों में छिपा कर । यह उत्तर दिखाता है कि पुनरावृत्तियों के साथ आप काफी लचीले हैं।
हालाँकि यह एक पूर्व-सॉर्ट की गई सूची थी, इसलिए यह आपके उदाहरण के लिए उपयुक्त नहीं होगा। इसके अतिरिक्त मैं हमेशा मानक वृक्ष संरचना और HTML <ul>
और <li>
तत्वों के लिए इसे हल करना चाहता था ।
मैं आया मूल अवधारणा निम्नलिखित है:
TreeNode
- प्रत्येक तत्व को एक सरल TreeNode
प्रकार में सार करता है जो इसे मूल्य प्रदान कर सकता है (जैसे Name
) और इसके बच्चे हैं या नहीं।
TreeNodesIterator
- RecursiveIterator
जो इनमें से एक सेट (सरणी) पर पुनरावृति करने में सक्षम है TreeNodes
। यह काफी सरल है क्योंकि TreeNode
प्रकार पहले से ही जानता है कि क्या इसमें बच्चे और कौन से हैं।
RecursiveListIterator
- RecursiveIteratorIterator
जब किसी भी तरह की पुनरावृत्ति हो, तो उसके लिए आवश्यक सभी घटनाएँ RecursiveIterator
:
beginIteration
/ endIteration
- मुख्य सूची का आरंभ और अंत।
beginElement
/ endElement
- प्रत्येक तत्व का आरंभ और अंत।
beginChildren
/ endChildren
- प्रत्येक बच्चों की सूची का आरंभ और अंत। यह RecursiveListIterator
केवल फ़ंक्शन कॉल के रूप में इन घटनाओं को प्रदान करता है। बच्चों की सूची, क्योंकि यह <ul><li>
सूचियों के लिए विशिष्ट है , इसे खोला और बंद किया गया है, यह मूल <li>
तत्व है। इसलिए endElement
घटना के अनुसार endChildren
घटना को निकाल दिया जाता है । इस वर्ग के उपयोग को व्यापक बनाने के लिए इसे बदला या कॉन्फ़िगर किया जा सकता है। घटनाओं को एक डेकोरेटर ऑब्जेक्ट के लिए फ़ंक्शन कॉल के रूप में वितरित किया जाता है, फिर चीजों को अलग रखने के लिए।
ListDecorator
- एक "डेकोरेटर" वर्ग जो केवल घटनाओं की एक रिसीवर है RecursiveListIterator
।
मैं मुख्य आउटपुट लॉजिक से शुरू करता हूं। अब पदानुक्रमित $tree
सरणी लिया , अंतिम कोड निम्न की तरह दिखता है:
$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());
}
में सबसे पहले आइए नज़र ListDecorator
कि बस लपेटता <ul>
और <li>
तत्वों और कैसे सूची संरचना उत्पादन होता है के बारे में निर्णय लेने से है:
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);
}
कंस्ट्रक्टर इस पर काम कर रहे सूची पुनरावृत्ति लेता है। inset
आउटपुट के अच्छे इंडेंटेशन के लिए सिर्फ एक सहायक कार्य है। बाकी प्रत्येक घटना के लिए सिर्फ उत्पादन कार्य हैं:
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());
}
}
इन आउटपुट फ़ंक्शंस को ध्यान में रखते हुए, यह मुख्य आउटपुट रैप-अप / लूप है, मैं इसे चरण दर चरण आगे बढ़ाता हूं:
$root = new TreeNode($tree);
उस रूट को बनाएँ, TreeNode
जिसका उपयोग पुनरावृति शुरू करने के लिए किया जाएगा:
$it = new TreeNodesIterator(array($root));
यह TreeNodesIterator
एक है RecursiveIterator
कि एक से अधिक पुनरावर्ती यात्रा में सक्षम बनाता है $root
नोड। इसे एक सरणी के रूप में पारित किया गया है क्योंकि उस वर्ग को कुछ अधिक करने की आवश्यकता है और बच्चों के एक सेट के साथ पुन: उपयोग करने की अनुमति देता है जो TreeNode
तत्वों का एक सरणी भी है ।
$rit = new RecursiveListIterator($it);
यह RecursiveListIterator
एक है RecursiveIteratorIterator
कहा घटनाओं में प्रावधान है कि। इसका उपयोग करने के लिए, केवल ListDecorator
(ऊपर वर्ग) प्रदान करने की आवश्यकता है और इसके साथ सौंपा गया है addDecorator
:
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
तब सब कुछ बस foreach
इसके ऊपर सेट होता है और प्रत्येक नोड को आउटपुट करता है:
foreach($rit as $item)
{
$inset = $decor->inset(1);
printf("%s%s\n", $inset, $item->getName());
}
जैसा कि इस उदाहरण से पता चलता है, पूरे आउटपुट लॉजिक को ListDecorator
कक्षा में समझाया गया है और यह एकल है foreach
। पूरे पुनरावर्ती ट्रैवर्सल को पूरी तरह से एसपीएल पुनरावर्ती पुनरावृत्तियों में संलग्न किया गया है जो एक स्टैक्ड प्रक्रिया प्रदान करता है, जिसका अर्थ है कि आंतरिक रूप से कोई पुनरावृत्ति फ़ंक्शन कॉल नहीं किया जाता है।
आधारित घटना ListDecorator
आपको विशेष रूप से आउटपुट को संशोधित करने और एक ही डेटा संरचना के लिए कई प्रकार की सूची प्रदान करने की अनुमति देती है। इनपुट को बदलना संभव है क्योंकि सरणी डेटा को इनकैप्सुलेट किया गया है TreeNode
।
पूर्ण कोड उदाहरण:
<?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>
डेमो (PHP 5.2 संस्करण)
एक संभावित संस्करण RecursiveIterator
एक पुनरावृत्त होगा जो किसी भी पर पुनरावृत्त होता है और सभी घटनाओं पर एक पुनरावृत्ति प्रदान करता है जो घटित हो सकते हैं। फ़ॉरच लूप के अंदर एक स्विच / केस तब घटनाओं से निपट सकता था।
सम्बंधित: