Mengonversi rangkaian hubungan induk-anak menjadi pohon hierarki?

100

Saya memiliki banyak pasangan nama-orang tua, yang ingin saya ubah menjadi sesedikit mungkin struktur pohon pusaka. Jadi misalnya, ini bisa berupa pasangan:

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

Yang perlu diubah menjadi (a) pohon heirarki:

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

Hasil akhir yang saya inginkan adalah sekumpulan <ul>elemen bersarang , dengan masing <li>- masing berisi nama anak.

Tidak ada ketidakkonsistenan dalam penyandingan (anak adalah orang tua sendiri, orang tua adalah anak anak, dll), sehingga banyak pengoptimalan mungkin dapat dilakukan.

Bagaimana, dalam PHP, saya akan beralih dari array yang berisi anak => pasangan induk, ke satu set Nested <ul>?

Saya merasa bahwa rekursi terlibat, tetapi saya tidak cukup bangun untuk memikirkannya.

Eric
sumber

Jawaban:

129

Ini membutuhkan fungsi rekursif yang sangat dasar untuk mengurai pasangan anak / induk ke struktur pohon dan fungsi rekursif lain untuk mencetaknya. Hanya satu fungsi yang cukup tetapi berikut dua untuk kejelasan (fungsi gabungan dapat ditemukan di akhir jawaban ini).

Pertama-tama, inisialisasi larik pasangan anak / induk:

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

Kemudian fungsi yang mem-parsing array itu menjadi struktur hierarki pohon:

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;    
}

Dan fungsi yang melintasi pohon itu untuk mencetak daftar yang tidak berurutan:

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>';
    }
}

Dan penggunaan sebenarnya:

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

Berikut isi dari $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
                            )
                        )
                    )
                )
            )
        )
    )
)

Jika Anda ingin sedikit lebih efisien, Anda dapat menggabungkan fungsi-fungsi itu menjadi satu dan mengurangi jumlah iterasi yang dibuat:

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>';
    }
}

Anda hanya akan menyimpan 8 iterasi pada kumpulan data sekecil ini tetapi pada kumpulan yang lebih besar itu bisa membuat perbedaan.

Tatu Ulmanen
sumber
2
Tatu. Bagaimana saya bisa mengubah fungsi printTree agar tidak langsung menggemakan html pohon tetapi menyimpan semua html keluaran ke dalam variabel dan mengembalikannya? terima kasih
Enrique
Hai, Saya pikir deklarasi fungsi harus parseAndPrintTree ($ tree, $ root = null) dan panggilan rekursif harus parseAndPrintTree ($ child, $ tree); Salam
pisau cukur7
55

Namun Fungsi Lain Untuk Membuat Pohon (tidak ada rekursi yang terlibat, sebagai gantinya menggunakan referensi):

$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;
}

Mengembalikan larik hierarki seperti ini:

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

Yang dapat dengan mudah dicetak sebagai daftar HTML menggunakan fungsi rekursif.

Alexander Konstantinov
sumber
+1 - Sangat pintar. Meskipun saya menemukan solusi rekursif lebih logis. Tapi saya lebih suka format keluaran fungsi Anda.
Eric
@Elebih logis? Saya mohon untuk berbeda. Tidak ada yang 'logis' dalam rekursi; ada OTOH kelebihan kognitif yang parah saat mengurai fungsi / panggilan rekursif. Jika tidak ada alokasi tumpukan eksplisit, saya akan mengambil iterasi daripada rekursi setiap hari.
29

Cara lain yang lebih disederhanakan untuk mengubah struktur datar $treemenjadi hierarki. Hanya satu larik sementara yang diperlukan untuk mengeksposnya:

// 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);

Itu saja untuk membuat hierarki menjadi array multidimensi:

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
)

Outputnya kurang sepele jika Anda ingin menghindari rekursi (bisa menjadi beban dengan struktur besar).

Saya selalu ingin memecahkan "dilema" UL / LI untuk mengeluarkan sebuah array. Dilemanya adalah, setiap item tidak mengetahui apakah anak akan menindaklanjuti atau tidak atau berapa banyak elemen sebelumnya yang perlu ditutup. Dalam jawaban lain saya sudah memecahkannya dengan menggunakan RecursiveIteratorIteratordan mencari getDepth()dan meta-informasi lain yang saya tulis sendiri Iterator: Mendapatkan model set bersarang ke dalam <ul>tetapi menyembunyikan subpohon "tertutup" . Itu jawaban menunjukkan juga bahwa dengan iterator Anda cukup fleksibel.

Namun itu adalah daftar yang telah diurutkan sebelumnya, jadi tidak akan cocok untuk contoh Anda. Selain itu saya selalu ingin menyelesaikan ini untuk semacam struktur pohon standar dan HTML <ul>dan <li>elemen.

Konsep dasar yang saya kemukakan adalah sebagai berikut:

  1. TreeNode- Mengabstraksi setiap elemen menjadi TreeNodetipe sederhana yang dapat memberikan nilainya (misalnya Name) dan memiliki turunan atau tidak.
  2. TreeNodesIterator- A RecursiveIteratoryang mampu melakukan iterasi atas satu set (array) ini TreeNodes. Itu cukup sederhana karena TreeNodetipe sudah tahu apakah ia memiliki anak dan yang mana.
  3. RecursiveListIterator- A RecursiveIteratorIteratoryang memiliki semua peristiwa yang diperlukan saat iterasi secara rekursif melalui segala jenis RecursiveIterator:
    • beginIteration/ endIteration- Awal dan akhir daftar utama.
    • beginElement/ endElement- Awal dan akhir setiap elemen.
    • beginChildren/ endChildren- Awal dan akhir setiap daftar anak. Ini RecursiveListIteratorhanya menyediakan acara ini dalam bentuk pemanggilan fungsi. daftar anak-anak, seperti yang biasa untuk <ul><li>daftar, dibuka dan ditutup di dalam <li>elemen induknya . Oleh karena itu endElementacara tersebut dipecat setelah endChildrenacara yang sesuai . Ini dapat diubah atau dibuat dapat dikonfigurasi untuk memperluas penggunaan kelas ini. Peristiwa didistribusikan sebagai panggilan fungsi ke objek dekorator kemudian, untuk memisahkan hal-hal.
  4. ListDecorator- Kelas "dekorator" yang hanya menerima peristiwa RecursiveListIterator.

Saya mulai dengan logika keluaran utama. Diambil $treearray hirarkis sekarang , kode akhirnya terlihat seperti berikut:

$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());
}

Penampilan let pertama ke dalam ListDecoratoryang hanya membungkus <ul>dan <li>elemen dan memutuskan tentang bagaimana struktur daftar adalah output:

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);
    }

Konstruktor mengambil iterator daftar yang sedang dikerjakannya. insethanyalah fungsi pembantu untuk lekukan yang bagus pada keluaran. Sisanya hanyalah fungsi keluaran untuk setiap acara:

    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());
    }
}

Dengan mengingat fungsi-fungsi output ini, ini adalah output utama / loop lagi, saya melakukannya selangkah demi selangkah:

$root = new TreeNode($tree);

Buat root TreeNodeyang akan digunakan untuk memulai iterasi:

$it = new TreeNodesIterator(array($root));

Ini TreeNodesIteratoradalah RecursiveIteratoryang memungkinkan iterasi rekursif melalui satu $rootnode. Itu diteruskan sebagai array karena kelas itu membutuhkan sesuatu untuk diiterasi dan memungkinkan penggunaan kembali dengan satu set turunan yang juga merupakan array TreeNodeelemen.

$rit = new RecursiveListIterator($it);

Ini RecursiveListIteratoradalah RecursiveIteratorIteratoryang menyediakan acara tersebut. Untuk memanfaatkannya, hanya ListDecoratorperlu disediakan (kelas di atas) dan ditugaskan dengan addDecorator:

$decor = new ListDecorator($rit);
$rit->addDecorator($decor);

Kemudian semuanya diatur ke foreachatasnya dan mengeluarkan setiap node:

foreach($rit as $item)
{
    $inset = $decor->inset(1);
    printf("%s%s\n", $inset, $item->getName());
}

Seperti yang ditunjukkan contoh ini, seluruh logika output dikemas dalam ListDecoratorkelas dan single ini foreach. Seluruh traversal rekursif telah sepenuhnya dikemas ke dalam iterator rekursif SPL yang menyediakan prosedur bertumpuk, yang berarti secara internal tidak ada panggilan fungsi rekursi yang dilakukan.

Berbasis peristiwa ListDecoratormemungkinkan Anda mengubah keluaran secara khusus dan menyediakan beberapa jenis daftar untuk struktur data yang sama. Bahkan dimungkinkan untuk mengubah input karena data array telah dienkapsulasi TreeNode.

Contoh kode lengkap:

<?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 (varian PHP 5.2)

Varian yang mungkin adalah iterator yang melakukan iterasi atas apa pun RecursiveIteratordan memberikan iterasi atas semua peristiwa yang dapat terjadi. Sebuah switch / case di dalam foreach loop kemudian dapat menangani kejadian tersebut.

Terkait:

hakre
sumber
3
Solusi ini "direkayasa dengan baik" - seberapa tepatnya itu "cara yang lebih sederhana" daripada contoh sebelumnya - Sepertinya solusi yang terlalu direkayasa untuk masalah yang sama
Andre
@Andre: Berdasarkan tingkat enkapsulasi IIRC. Dalam jawaban lain yang terkait, saya memiliki fragmen kode non-enkapsulasi yang jauh lebih kecil dan oleh karena itu mungkin "lebih disederhanakan" tergantung pada POV.
hakre
@hakre Bagaimana saya dapat memodifikasi kelas "ListDecorator" untuk menambahkan 'id' ke LI, yang sedang diambil dari array pohon?
Gangesh
1
@ Gangesh: Paling mudah dengan vistor node. ^^ Bercanda sedikit, langsung saja adalah memperluas dekorator dan mengedit beginElement (), dapatkan iterator bagian dalam (lihat metode inset () sebagai contoh) dan pekerjaan dengan atribut id.
hakre
@ Terima kasih. Saya akan mencobanya.
Gangesh
8

Nah, pertama-tama saya akan mengubah array lurus dari pasangan nilai kunci menjadi array hierarki

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];
}

Itu akan dapat mengubah array datar dengan parent_id dan id menjadi hierarki:

$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(),
                ),
            ),
        ),
    ),
);

Kemudian, buat saja fungsi rendering:

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;
}
ircmaxell
sumber
5

Meskipun solusi Alexander-Konstantinov mungkin tampak tidak mudah untuk dibaca pada awalnya, namun solusi tersebut sangat jenius dan secara eksponensial lebih baik dalam hal kinerja, ini seharusnya dipilih sebagai jawaban terbaik.

Terima kasih sobat, saya membuat patokan untuk menghormati Anda untuk membandingkan 2 solusi yang disajikan dalam posting ini.

Saya memiliki pohon datar @ 250k dengan 6 level yang harus saya konversi dan saya sedang mencari cara yang lebih baik untuk melakukannya dan menghindari iterasi rekursif.

Rekursi vs Referensi:

// 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;

Outputnya berbicara sendiri:

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

Nah, untuk mengurai menjadi UL dan LI, itu akan menjadi seperti:

$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>';
}

Tetapi saya ingin melihat solusi yang tidak mengharuskan Anda untuk berulang-ulang melalui array begitu sering ...

arnorhs
sumber
2

Inilah yang saya dapatkan:

$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
      }
    }

keluaran:

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

            [E] => Array
                (
                    [A] => A
                    [C] => Array
                        (
                            [B] => B
                        )    
                )    
        )    
)
Dan Heberden
sumber
1

Hubungan induk-anak bersarang Array
Ambil semua record dari database dan membuat array bersarang.

$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;
}

Cetak data Kategori dan Subkategori dalam format 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
    ];
}
Akshay Mistry
sumber
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'
);

$ aa = $ this-> parseTree ($ tree);

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>";
        }
    }
Bharat
sumber
0

Pertanyaan lama, tetapi saya juga harus melakukan ini dan contoh dengan rekursi membuat saya sakit kepala. Dalam database saya, kami memiliki locationstabel, yang merupakan loca_idPK (Anak) dan referensi mandiri loca_parent_id(Induk).

Tujuannya adalah untuk merepresentasikan struktur ini dalam HTML. Permintaan sederhana dari couyrse mengembalikan data adalah beberapa urutan tetap tetapi saya menemukan tidak cukup baik untuk menampilkan data tersebut secara alami. Yang benar-benar saya inginkan adalah penanganan berjalan pohon Oracle LEVELuntuk membantu tampilan.

Saya memutuskan untuk menggunakan gagasan 'jalur' untuk mengidentifikasi setiap entri secara unik. Sebagai contoh:

Menyortir larik berdasarkan jalur akan mempermudah proses untuk tampilan yang bermakna.

Saya menyadari bahwa penggunaan array dan jenis asosiatif adalah curang karena menyembunyikan kompleksitas rekursif dari operasi, tetapi bagi saya ini terlihat lebih sederhana:

<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>
TenG
sumber
-1

Cara Membuat Tampilan dan Menu Pohon Dinamis

Langkah 1: Pertama kita akan membuat tabel treeview di database mysql. tabel ini berisi empat kolom.id adalah id tugas dan nama adalah nama tugas.

-
-- 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');

Langkah 2: Metode rekursif tampilan pohon yang telah saya buat di bawah metode createTreeView () pohon yang memanggil rekursif jika id tugas saat ini lebih besar dari id tugas sebelumnya.

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> ";

}

Langkah 3: Buat file indeks untuk menampilkan tampilan struktur pohon. Ini adalah file utama dari contoh treeview di sini kita akan memanggil metode createTreeView () dengan parameter yang diperlukan.

 <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>

Langkah 4: Buat file CSS style.css Di sini kita akan menulis semua kelas terkait css, saat ini saya menggunakan daftar pesanan untuk membuat tampilan pohon. Anda juga dapat mengubah jalur gambar di sini.

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 */ }

Keterangan lebih lanjut


sumber