माता-पिता-बाल संबंधों की एक श्रृंखला को एक पदानुक्रमित पेड़ में परिवर्तित करें?


100

मेरे पास नाम-पैरेंटनेम जोड़े का एक गुच्छा है, जो कि मैं संभव के रूप में कुछ उत्तराधिकारी वृक्ष संरचनाओं में बदलना चाहता हूं। उदाहरण के लिए, ये जोड़ियाँ हो सकती हैं:

Child : Parent
    H : G
    F : G
    G : D
    E : D
    A : E
    B : C
    C : E
    D : NULL

जिसे (ए) पितृसत्तात्मक वृक्ष में बदलना होगा:

D
├── E
   ├── A
      └── B
   └── C   
└── G
    ├── F
    └── H

अंतिम परिणाम जो मुझे चाहिए <ul>, प्रत्येक के साथ तत्वों का एक नेस्टेड सेट है<li> में बच्चे का नाम होता है।

जोड़ियों में कोई विसंगतियां नहीं हैं (बच्चा यह स्वयं का माता-पिता है, माता-पिता बच्चे का बच्चा है, आदि), इसलिए शायद अनुकूलन का एक गुच्छा बनाया जा सकता है।

कैसे, PHP में, मैं एक ऐसे सरणी से जाऊँगा जिसमें बच्चे => माता-पिता के जोड़े हों, जो नेस्टेड के सेट पर हों <ul> s के ?

मुझे लगता है कि पुनरावृत्ति शामिल है, लेकिन मैं इसके माध्यम से सोचने के लिए पर्याप्त जागृत नहीं हूं।

जवाबों:


129

इसके लिए बच्चे / माता-पिता के जोड़े को एक पेड़ की संरचना और एक अन्य पुनरावर्ती फ़ंक्शन को पार्स करने के लिए एक बहुत ही मूल पुनरावर्ती फ़ंक्शन की आवश्यकता होती है। केवल एक फ़ंक्शन पर्याप्त होगा लेकिन यहाँ स्पष्टता के लिए दो (इस उत्तर के अंत में एक संयुक्त फ़ंक्शन पाया जा सकता है)।

पहले बच्चे / माता-पिता की जोड़ी को आरंभीकृत करें:

$tree = array(
    'H' => 'G',
    'F' => 'G',
    'G' => 'D',
    'E' => 'D',
    'A' => 'E',
    'B' => 'C',
    'C' => 'E',
    'D' => null
);

फिर वह कार्य जो उस सरणी को एक पदानुक्रमित वृक्ष संरचना में बनाता है:

function parseTree($tree, $root = null) {
    $return = array();
    # Traverse the tree and search for direct children of the root
    foreach($tree as $child => $parent) {
        # A direct child is found
        if($parent == $root) {
            # Remove item from tree (we don't need to traverse this again)
            unset($tree[$child]);
            # Append the child into result array and parse its children
            $return[] = array(
                'name' => $child,
                'children' => parseTree($tree, $child)
            );
        }
    }
    return empty($return) ? null : $return;    
}

और एक ऐसा फंक्शन जो उस पेड़ को एक अनियंत्रित सूची को प्रिंट करने के लिए ट्रेस करता है:

function printTree($tree) {
    if(!is_null($tree) && count($tree) > 0) {
        echo '<ul>';
        foreach($tree as $node) {
            echo '<li>'.$node['name'];
            printTree($node['children']);
            echo '</li>';
        }
        echo '</ul>';
    }
}

और वास्तविक उपयोग:

$result = parseTree($tree);
printTree($result);

यहाँ की सामग्री है $result:

Array(
    [0] => Array(
        [name] => D
        [children] => Array(
            [0] => Array(
                [name] => G
                [children] => Array(
                    [0] => Array(
                        [name] => H
                        [children] => NULL
                    )
                    [1] => Array(
                        [name] => F
                        [children] => NULL
                    )
                )
            )
            [1] => Array(
                [name] => E
                [children] => Array(
                    [0] => Array(
                        [name] => A
                        [children] => NULL
                    )
                    [1] => Array(
                        [name] => C
                        [children] => Array(
                            [0] => Array(
                                [name] => B
                                [children] => NULL
                            )
                        )
                    )
                )
            )
        )
    )
)

यदि आप थोड़ी अधिक दक्षता चाहते हैं, तो आप उन कार्यों को एक में जोड़ सकते हैं और किए गए पुनरावृत्तियों की संख्या को कम कर सकते हैं:

function parseAndPrintTree($root, $tree) {
    $return = array();
    if(!is_null($tree) && count($tree) > 0) {
        echo '<ul>';
        foreach($tree as $child => $parent) {
            if($parent == $root) {                    
                unset($tree[$child]);
                echo '<li>'.$child;
                parseAndPrintTree($child, $tree);
                echo '</li>';
            }
        }
        echo '</ul>';
    }
}

आप एक डेटासेट पर केवल 8 पुनरावृत्तियों को इस रूप में छोटा करेंगे, लेकिन बड़े सेटों पर इससे फर्क पड़ सकता है।


2
Tatu। मैं प्रिंटट्री फ़ंक्शन को कैसे बदल सकता हूं ताकि सीधे पेड़ के HTML को प्रतिध्वनित न किया जा सके लेकिन सभी आउटपुट HTML को एक चर में सहेज कर वापस कर दिया जाए? धन्यवाद
Enrique

नमस्ते, मुझे लगता है कि फ़ंक्शन घोषणा को parseAndPrintTree ($ ट्री, $ रूट = null) होना चाहिए और पुनरावर्ती कॉल parseAndPrintTree ($ बच्चा, $ ट्री) होगा; सादर
razor7

55

फिर भी एक पेड़ बनाने के लिए एक और कार्य (कोई पुनरावृत्ति शामिल नहीं है, इसके बजाय संदर्भ का उपयोग करता है):

$array = array('H' => 'G', 'F' => 'G', ..., 'D' => null);

function to_tree($array)
{
    $flat = array();
    $tree = array();

    foreach ($array as $child => $parent) {
        if (!isset($flat[$child])) {
            $flat[$child] = array();
        }
        if (!empty($parent)) {
            $flat[$parent][$child] =& $flat[$child];
        } else {
            $tree[$child] =& $flat[$child];
        }
    }

    return $tree;
}

इस तरह एक श्रेणीबद्ध सरणी देता है:

Array(
    [D] => Array(
        [G] => Array(
            [H] => Array()
            [F] => Array()
        )
        ...
    )
)

जिसे आसानी से पुनरावर्ती फ़ंक्शन का उपयोग करके HTML सूची के रूप में मुद्रित किया जा सकता है।


+1 - बहुत चालाक। हालांकि मुझे पुनरावर्ती समाधान अधिक तार्किक लगता है। लेकिन मैं आपके फ़ंक्शन के आउटपुट स्वरूप को प्राथमिकता देता हूं।
एरिक

@ अधिक तार्किक? क्षमा करें मैं असहमत हूं। पुनरावृत्ति में 'तार्किक' कुछ भी नहीं है; OTOH पुनरावर्ती कार्यों / कॉल को पार्स करने पर एक गंभीर संज्ञानात्मक ओवरहेड है। यदि कोई स्पष्ट स्टैक आवंटन नहीं है, तो मैं हर दिन पुनरावृत्ति पर पुनरावृत्ति ले जाऊंगा।


29

फ्लैट संरचना को $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>तत्वों के लिए इसे हल करना चाहता था ।

मैं आया मूल अवधारणा निम्नलिखित है:

  1. TreeNode- प्रत्येक तत्व को एक सरल TreeNodeप्रकार में सार करता है जो इसे मूल्य प्रदान कर सकता है (जैसे Name) और इसके बच्चे हैं या नहीं।
  2. TreeNodesIterator- RecursiveIteratorजो इनमें से एक सेट (सरणी) पर पुनरावृति करने में सक्षम है TreeNodes। यह काफी सरल है क्योंकि TreeNodeप्रकार पहले से ही जानता है कि क्या इसमें बच्चे और कौन से हैं।
  3. RecursiveListIterator- RecursiveIteratorIteratorजब किसी भी तरह की पुनरावृत्ति हो, तो उसके लिए आवश्यक सभी घटनाएँ RecursiveIterator:
    • beginIteration/ endIteration- मुख्य सूची का आरंभ और अंत।
    • beginElement/ endElement- प्रत्येक तत्व का आरंभ और अंत।
    • beginChildren/ endChildren- प्रत्येक बच्चों की सूची का आरंभ और अंत। यह RecursiveListIteratorकेवल फ़ंक्शन कॉल के रूप में इन घटनाओं को प्रदान करता है। बच्चों की सूची, क्योंकि यह <ul><li>सूचियों के लिए विशिष्ट है , इसे खोला और बंद किया गया है, यह मूल <li>तत्व है। इसलिए endElementघटना के अनुसार endChildrenघटना को निकाल दिया जाता है । इस वर्ग के उपयोग को व्यापक बनाने के लिए इसे बदला या कॉन्फ़िगर किया जा सकता है। घटनाओं को एक डेकोरेटर ऑब्जेक्ट के लिए फ़ंक्शन कॉल के रूप में वितरित किया जाता है, फिर चीजों को अलग रखने के लिए।
  4. 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एक पुनरावृत्त होगा जो किसी भी पर पुनरावृत्त होता है और सभी घटनाओं पर एक पुनरावृत्ति प्रदान करता है जो घटित हो सकते हैं। फ़ॉरच लूप के अंदर एक स्विच / केस तब घटनाओं से निपट सकता था।

सम्बंधित:


3
जैसा कि इस समाधान के रूप में "अच्छी तरह से इंजीनियर" है - पिछले उदाहरणों की तुलना में यह "अधिक सरल तरीका" कैसे है - यह सिर्फ उसी समस्या के लिए एक इंजीनियर समाधान की तरह लगता है
आंद्रे

@Andre: एनकैप्सुलेशन IIRC के ग्रेड द्वारा। एक अन्य संबंधित उत्तर में मेरे पास पूरी तरह से गैर-एनकैप्सुलेटेड कोड-टुकड़ा है जो बहुत छोटा है और इसलिए पीओवी के आधार पर "अधिक सरलीकृत" हो सकता है।
हकर्रे

@hakre मैं "ListDecorator" वर्ग को LI में 'id' जोड़ने के लिए कैसे संशोधित कर सकता हूं, जिसे ट्री एरे से प्राप्त किया जा रहा है?
गंगेश

1
@ गणेश: एक नोड विस्टर के साथ सबसे आसानी से। ^ ^ थोड़ा मजाक करते हुए, सीधे आगे डेकोरेटर का विस्तार करना है और शुरुआती एडिटमेंट () को संपादित करना है, इनर इटरेटर (उदाहरण के लिए इनसेट () विधि देखें) और आईडी विशेषता के साथ काम करें।
हकरे

@ ठकरे धन्यवाद। मैं कोशिश करूँगा कि
गंगेश

8

खैर, पहले मैं कुंजी-मूल्य जोड़े के सीधे सरणी को एक श्रेणीबद्ध सरणी में बदल दूंगा

function convertToHeiarchical(array $input) {
    $parents = array();
    $root = array();
    $children = array();
    foreach ($input as $item) {
        $parents[$item['id']] = &$item;
        if ($item['parent_id']) {
            if (!isset($children[$item['parent_id']])) {
                $children[$item['parent_id']] = array();
            }
            $children[$item['parent_id']][] = &$item;
        } else {
            $root = $item['id'];
        }
    }
    foreach ($parents as $id => &$item) {
        if (isset($children[$id])) {
            $item['children'] = $children[$id];
        } else {
            $item['children'] = array();
        }
    }
    return $parents[$root];
}

यह माता-पिता के साथ एक फ्लैट सरणी और आईडी को एक श्रेणीबद्ध में बदल सकता है:

$item = array(
    'id' => 'A',
    'blah' => 'blah',
    'children' => array(
        array(
            'id' => 'B',
            'blah' => 'blah',
            'children' => array(
                array(
                    'id' => 'C',
                    'blah' => 'blah',
                    'children' => array(),
                ),
             ),
            'id' => 'D',
            'blah' => 'blah',
            'children' => array(
                array(
                    'id' => 'E',
                    'blah' => 'blah',
                    'children' => array(),
                ),
            ),
        ),
    ),
);

फिर, बस एक रेंडरिंग फंक्शन बनाएं:

function renderItem($item) {
    $out = "Your OUtput For Each Item Here";
    $out .= "<ul>";
    foreach ($item['children'] as $child) {
        $out .= "<li>".renderItem($child)."</li>";
    }
    $out .= "</ul>";
    return $out;
}

5

हालांकि अलेक्जेंडर-कोंस्टेंटिनोव के समाधान को पहली बार में पढ़ना उतना आसान नहीं लग सकता है, यह प्रदर्शन के मामले में प्रतिभाशाली और तेजी से बेहतर है, इसे सबसे अच्छे उत्तर के रूप में वोट दिया जाना चाहिए।

धन्यवाद दोस्त, मैंने इस पद पर प्रस्तुत २ समाधानों की तुलना करने के लिए आपके सम्मान में एक बेंचमार्क बनाया।

मेरे पास 6 स्तरों वाला एक @ 250k फ्लैट का पेड़ था जिसे मुझे परिवर्तित करना था और मैं ऐसा करने के लिए एक बेहतर तरीका खोज रहा था और पुनरावर्ती पुनरावृत्तियों से बचता था।

संदर्भ बनाम संदर्भ:

// Generate a 6 level flat tree
$root = null;
$lvl1 = 13;
$lvl2 = 11;
$lvl3 = 7;
$lvl4 = 5;
$lvl5 = 3;
$lvl6 = 1;    
$flatTree = [];
for ($i = 1; $i <= 450000; $i++) {
    if ($i % 3 == 0)  { $lvl5 = $i; $flatTree[$lvl6] = $lvl5; continue; }
    if ($i % 5 == 0)  { $lvl4 = $i; $flatTree[$lvl5] = $lvl4; continue; }
    if ($i % 7 == 0)  { $lvl3 = $i; $flatTree[$lvl3] = $lvl2; continue; }
    if ($i % 11 == 0) { $lvl2 = $i; $flatTree[$lvl2] = $lvl1; continue; }
    if ($i % 13 == 0) { $lvl1 = $i; $flatTree[$lvl1] = $root; continue; }
    $lvl6 = $i;
}

echo 'Array count: ', count($flatTree), PHP_EOL;

// Reference function
function treeByReference($flatTree)
{
    $flat = [];
    $tree = [];

    foreach ($flatTree as $child => $parent) {
        if (!isset($flat[$child])) {
            $flat[$child] = [];
        }
        if (!empty($parent)) {
            $flat[$parent][$child] =& $flat[$child];
        } else {
            $tree[$child] =& $flat[$child];
        }
    }

    return $tree;
}

// Recursion function
function treeByRecursion($flatTree, $root = null)
{
    $return = [];
    foreach($flatTree as $child => $parent) {
        if ($parent == $root) {
            unset($flatTree[$child]);
            $return[$child] = treeByRecursion($flatTree, $child);
        }
    }
    return $return ?: [];
}

// Benchmark reference
$t1 = microtime(true);
$tree = treeByReference($flatTree);
echo 'Reference: ', (microtime(true) - $t1), PHP_EOL;

// Benchmark recursion
$t2 = microtime(true);
$tree = treeByRecursion($flatTree);
echo 'Recursion: ', (microtime(true) - $t2), PHP_EOL;

आउटपुट स्वयं बोलता है:

Array count: 255493
Reference: 0.3259289264679 (less than 0.4s)
Recursion: 6604.9865279198 (almost 2h)

2

ठीक है, ULs और LI में पार्स करने के लिए, यह कुछ इस तरह होगा:

$array = array (
    'H' => 'G'
    'F' => 'G'
    'G' => 'D'
    'E' => 'D'
    'A' => 'E'
    'B' => 'C'
    'C' => 'E'
    'D' => 'NULL'
);


recurse_uls ($array, 'NULL');

function recurse_uls ($array, $parent)
{
    echo '<ul>';
    foreach ($array as $c => $p)  {
        if ($p != $parent) continue;
        echo '<li>'.$c.'</li>';
        recurse_uls ($array, $c);
    }
    echo '</ul>';
}

लेकिन मैं एक ऐसा समाधान देखना पसंद करूंगा जिसमें आपको सरणी के माध्यम से इतनी बार पुनरावृति की आवश्यकता न हो ...


2

यहाँ मैं क्या लेकर आया हूँ:

$arr = array(
            'H' => 'G',
            'F' => 'G',
            'G' => 'D',
            'E' => 'D',
            'A' => 'E',
            'B' => 'C',
            'C' => 'E',
            'D' => null );

    $nested = parentChild($arr);
    print_r($nested);

    function parentChild(&$arr, $parent = false) {
      if( !$parent) { //initial call
         $rootKey = array_search( null, $arr);
         return array($rootKey => parentChild($arr, $rootKey));
      }else { // recursing through
        $keys = array_keys($arr, $parent);
        $piece = array();
        if($keys) { // found children, so handle them
          if( !is_array($keys) ) { // only one child
            $piece = parentChild($arr, $keys);
           }else{ // multiple children
             foreach( $keys as $key ){
               $piece[$key] = parentChild($arr, $key);
             }
           }
        }else {
           return $parent; //return the main tag (no kids)
        }
        return $piece; // return the array built via recursion
      }
    }

आउटपुट:

Array
(
    [D] => Array
        (
            [G] => Array
                (
                    [H] => H
                    [F] => F
                )

            [E] => Array
                (
                    [A] => A
                    [C] => Array
                        (
                            [B] => B
                        )    
                )    
        )    
)

1

अभिभावक-बच्चे के संबंध ने अर्रे
को डेटाबेस से सभी रिकॉर्ड प्राप्त किया और नेस्टेड सरणी बनाया।

$data = SampleTable::find()->all();
$tree = buildTree($data);
print_r($tree);

public function buildTree(array $elements, $parentId = 0) {
    $branch = array();
    foreach ($elements as $element) {
        if ($element['iParentId'] == $parentId) {
            $children =buildTree($elements, $element['iCategoriesId']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[] = $element;
        }
    }
    return $branch;
}

JSON प्रारूप में श्रेणियां और उप-श्रेणियां डेटा प्रिंट करें

public static function buildTree(array $elements, $parentId = 0){
    $branch = array();
    foreach($elements as $element){
        if($element['iParentId']==$parentId){
            $children =buildTree($elements, $element['iCategoriesId']);
            if ($children) {
                $element['children'] = $children;

            }
                $branch[] = array(
                    'iCategoriesId' => $element->iCategoriesId,
                    'iParentId'=>$element->iParentId,
                    'vCategoriesName'=>$element->vCategoriesName,
                    'children'=>$element->children,
            );
        }
    }
    return[
        $branch
    ];
}

0
$tree = array(
    'H' => 'G',
    'F' => 'G',
    'G' => 'D',
    'E' => 'D',
    'A' => 'E',
    'B' => 'C',
    'C' => 'E',
    'D' => null,
    'Z' => null,
    'MM' =>'Z',
    'KK' =>'Z',
    'MMM' =>'MM',
    // 'MM'=>'DDD'
);

आ = $ this-> parseTree ($ पेड़) $;

public function get_tress($tree,$key)
{

    $x=array();
    foreach ($tree as $keys => $value) {
        if($value==$key){
        $x[]=($keys);
        }
    }
    echo "<li>";
    foreach ($x as $ke => $val) {
    echo "<ul>";
        echo($val);
        $this->get_tress($tree,$val);
    echo "</ul>";
    }
    echo "</li>";


}
function parseTree($tree, $root = null) {

    foreach ($tree as $key => $value) {
        if($value==$root){

            echo "<ul>";
            echo($key);
            $this->get_tress($tree,$key);
            echo "</ul>";
        }
    }

0

पुराना सवाल है, लेकिन मुझे भी ऐसा करना पड़ा और पुनरावृत्ति के साथ उदाहरणों ने मुझे सिरदर्द दिया। मेरे डेटाबेस में हमारे पास एक locationsटेबल है, जो एक loca_idपीके (चाइल्ड) और सेल्फ रेफ़रिंग loca_parent_id(पेरेंट) थी।

उद्देश्य HTML में इस संरचना का प्रतिनिधित्व करना है। डेटा को लौटाने की सरल क्वेरी कुछ निश्चित क्रम हो सकती है, लेकिन मुझे इस तरह के डेटा को प्राकृतिक तरीके से प्रदर्शित करने के लिए पर्याप्त नहीं मिला। जो मैं वास्तव में चाहता था वह था LEVELप्रदर्शन के साथ मदद के लिए ओरेकल ट्री वॉक हैंडलिंग ।

मैंने प्रत्येक प्रविष्टि को विशिष्ट रूप से पहचानने के लिए एक 'पथ' के विचार का उपयोग करने का निर्णय लिया। उदाहरण के लिए:

पथ द्वारा सरणी को क्रमबद्ध करना सार्थक प्रदर्शन के लिए प्रक्रिया को आसान बनाना चाहिए।

मुझे एहसास हुआ कि साहचर्य सरणियों और प्रकारों का उपयोग धोखा दे रहा है क्योंकि यह ऑपरेशन की पुनरावृत्ति जटिलता को छुपाता है, लेकिन मेरे लिए यह सरल है:

<table>
<?php
    
    $sql = "
    
    SELECT l.*,
           pl.loca_name parent_loca_name,
           '' loca_path
    FROM locations l
    LEFT JOIN locations pl ON l.loca_parent_id = pl.loca_id
    ORDER BY l.loca_parent_id, l.loca_id
    
    ";
    
    function print_row ( $rowdata )
    {
    ?>
                      <tr>
                          <td>
                              <?=$rowdata['loca_id']?>
                          </td>
                          <td>
                              <?=$rowdata['loca_path']?>
                          </td>
                          <td>
                              <?=$rowdata['loca_type']?>
                          </td>
                          <td>
                              <?=$rowdata['loca_status']?>
                          </td>
                      </tr>
    <?php
    
    }
    
    $stmt  = $dbh->prepare($sql);
    $stmt->execute();
    $result = $stmt->get_result();
    $data = $result->fetch_all(MYSQLI_ASSOC);
    
    $printed = array();
    
    // To get tree hierarchy usually means recursion of data.
    // Here we will try to use an associate array and set a
    // 'path' value to represent the hierarchy tree in one
    // pass. Sorting this array by the path value should give
    // a nice tree order and reference.
// The array key will be the unique id (loca_id) for each row.
// The value for each key will the complete row from the database.
// The row contains a element 'loca_path' - we will write the path
// for each row here. A child's path will be parent_path/child_name.
// For any child we encounter with a parent we look up the parents path
// using the loca_parent_id as the key.
// Caveat, although tested quickly, just make sure that all parents are
// returned first by the query.
    
    foreach ($data as $row)
    {
    
       if ( $row['loca_parent_id'] == '' ) // Root Parent
       {
          $row['loca_path'] = $row['loca_name'] . '/';
          $printed[$row['loca_id']] = $row;
       }
       else // Child/Sub-Parent
       {
          $row['loca_path'] = $printed[$row['loca_parent_id']]['loca_path'] . $row['loca_name'] . '/';
          $printed[$row['loca_id']] = $row;
       }
    }
    
    // Array with paths built, now sort then print
    
    array_multisort(array_column($printed, 'loca_path'), SORT_ASC, $printed);
    
    foreach ( $printed as $prow )
    {
       print_row ( $prow );
    }
    ?>
    </table>

-1

डायनामिक ट्री व्यू और मेनू कैसे बनाएं

चरण 1: सबसे पहले हम mysql डेटाबेस में ट्रीव्यू टेबल बनाएंगे। इस तालिका में चार कॉलम हैं। कार्य आईडी है और नाम कार्य नाम है।

-
-- Table structure for table `treeview_items`
--

CREATE TABLE IF NOT EXISTS `treeview_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  `title` varchar(200) NOT NULL,
  `parent_id` varchar(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `treeview_items`
--

INSERT INTO `treeview_items` (`id`, `name`, `title`, `parent_id`) VALUES
(1, 'task1', 'task1title', '2'),
(2, 'task2', 'task2title', '0'),
(3, 'task3', 'task1title3', '0'),
(4, 'task4', 'task2title4', '3'),
(5, 'task4', 'task1title4', '3'),
(6, 'task5', 'task2title5', '5');

चरण 2: ट्री व्यू रिकर्सिव विधि मैंने ट्री क्रिएटट्रीव्यू () विधि के नीचे बनाई है, जो मौजूदा टास्क आईडी की तुलना में वर्तमान कार्य आईडी से अधिक होने पर पुनरावर्ती कहलाती है।

function createTreeView($array, $currentParent, $currLevel = 0, $prevLevel = -1) {

foreach ($array as $categoryId => $category) {

if ($currentParent == $category['parent_id']) {                       
    if ($currLevel > $prevLevel) echo " <ol class='tree'> "; 

    if ($currLevel == $prevLevel) echo " </li> ";

    echo '<li> <label for="subfolder2">'.$category['name'].'</label> <input type="checkbox" name="subfolder2"/>';

    if ($currLevel > $prevLevel) { $prevLevel = $currLevel; }

    $currLevel++; 

    createTreeView ($array, $categoryId, $currLevel, $prevLevel);

    $currLevel--;               
    }   

}

if ($currLevel == $prevLevel) echo " </li>  </ol> ";

}

चरण 3: ट्री व्यू दिखाने के लिए इंडेक्स फ़ाइल बनाएं। यह ट्रीव्यू उदाहरण की मुख्य फ़ाइल है, यहाँ हम आवश्यक मापदंडों के साथ createTreeView () विधि को कॉल करेंगे।

 <body>
<link rel="stylesheet" type="text/css" href="_styles.css" media="screen">
<?php
mysql_connect('localhost', 'root');
mysql_select_db('test');


$qry="SELECT * FROM treeview_items";
$result=mysql_query($qry);


$arrayCategories = array();

while($row = mysql_fetch_assoc($result)){ 
 $arrayCategories[$row['id']] = array("parent_id" => $row['parent_id'], "name" =>                       
 $row['name']);   
  }
?>
<div id="content" class="general-style1">
<?php
if(mysql_num_rows($result)!=0)
{
?>
<?php 

createTreeView($arrayCategories, 0); ?>
<?php
}
?>

</div>
</body>

चरण 4: सीएसएस फ़ाइल शैली बनाएँ। यहाँ हम सभी सीएसएस संबंधित वर्ग लिखेंगे, वर्तमान में मैं ट्री व्यू बनाने के लिए ऑर्डर सूची का उपयोग कर रहा हूं। आप यहां छवि पथ भी बदल सकते हैं।

img { border: none; }
input, select, textarea, th, td { font-size: 1em; }

/* CSS Tree menu styles */
ol.tree
{
    padding: 0 0 0 30px;
    width: 300px;
}
    li 
    { 
        position: relative; 
        margin-left: -15px;
        list-style: none;
    }
    li.file
    {
        margin-left: -1px !important;
    }
        li.file a
        {
            background: url(document.png) 0 0 no-repeat;
            color: #fff;
            padding-left: 21px;
            text-decoration: none;
            display: block;
        }
        li.file a[href *= '.pdf']   { background: url(document.png) 0 0 no-repeat; }
        li.file a[href *= '.html']  { background: url(document.png) 0 0 no-repeat; }
        li.file a[href $= '.css']   { background: url(document.png) 0 0 no-repeat; }
        li.file a[href $= '.js']        { background: url(document.png) 0 0 no-repeat; }
    li input
    {
        position: absolute;
        left: 0;
        margin-left: 0;
        opacity: 0;
        z-index: 2;
        cursor: pointer;
        height: 1em;
        width: 1em;
        top: 0;
    }
        li input + ol
        {
            background: url(toggle-small-expand.png) 40px 0 no-repeat;
            margin: -0.938em 0 0 -44px; /* 15px */
            height: 1em;
        }
        li input + ol > li { display: none; margin-left: -14px !important; padding-left: 1px; }
    li label
    {
        background: url(folder-horizontal.png) 15px 1px no-repeat;
        cursor: pointer;
        display: block;
        padding-left: 37px;
    }

    li input:checked + ol
    {
        background: url(toggle-small.png) 40px 5px no-repeat;
        margin: -1.25em 0 0 -44px; /* 20px */
        padding: 1.563em 0 0 80px;
        height: auto;
    }
        li input:checked + ol > li { display: block; margin: 0 0 0.125em;  /* 2px */}
        li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }

अधिक जानकारी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.