рекурсивный обход дерева - как отслеживать уровень рекурсии?

Я в основном пытаюсь построить вложенный список HTML ul/li из многомерного массива, представляющего древовидную структуру.

следующий код отлично работает, но я хочу его улучшить:

мне нужен способ отслеживать уровень рекурсии, чтобы я мог применять разные классы к разным уровням,добавлять отступы к сгенерированному выходу и т. д.

function buildTree($tree_array, $display_field, $children_field, $class='', $id='') {

  echo "<ul>n";

  foreach ($tree_array as $row) {

    echo "<li>n";
    echo $row[$display_field] . "n";

    if (isset($row[$children_field])) {

      $this->buildTree($row[$children_field]);
    }
    echo "</li>n";
  }
  echo "</ul>n";
} 

$tree_array выглядит так:

Array
(
    [0] => Array
        (
            [category_id] => 1
            [category_name] => calculatoare
            [parent_id] => 0
            [children] => Array
                (
                    [0] => Array
                        (
                            [category_id] => 4
                            [category_name] => placi de baza
                            [parent_id] => 1
                        )

                    [1] => Array
                        (
                            [category_id] => 5
                            [category_name] => carcase
                            [parent_id] => 1
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [category_id] => 6
                                            [category_name] => midi-tower
                                            [parent_id] => 5
                                        )

                                )

                        )

                )

        )

    [1] => Array
        (
            [category_id] => 2
            [category_name] => electronice
            [parent_id] => 0
        )

    [2] => Array
        (
            [category_id] => 3
            [category_name] => carti
            [parent_id] => 0
        )

)

я пометил это как домашнее задание, потому что я хотелось бы использовать это как возможность улучшить мое (плохое) понимание рекурсии, поэтому я был бы признателен за ответы, которые привели бы меня к решению, а не предоставили полный рабочий пример :)

3 ответов


Quick'n'Dirty подход (см. Блок "спойлер" ниже для реализации):

добавить дополнительную переменную $recursionDepth для объявления функции сделайте его 0 по умолчанию.

при каждой последующей рекурсии вызывайте свою функцию с помощью $recursionDepth + 1.

поскольку переменные функции являются только "видимыми" (областью действия) для соответствующего экземпляра функции, вы получите индикатор текущей глубины итерации.

кроме того, строка 12 вашего функция

$this->buildTree();

не похоже, что это сработает – причина в том, что вы не передаете свои переменные следующему экземпляру buildTree.

это, вероятно, должно выглядеть так:

$this->buildTree($row[$children_field], $display_field, $children_field, $class, $id)

вот изменения, которые я бы внес в ваш код, чтобы достичь того, что вы хотите:

function buildTree($tree_array, $display_field, $children_field, $class='', $id='', $recursionDepth = 0, $maxDepth = false)
{
    if ($maxDepth && ($recursionDepth == $maxDepth)) return;

    echo "<ul>\n";

    foreach ($tree_array as $row)
    {
        echo "<li>\n";
        echo $row[$display_field] . "\n";

        if (isset($row[$children_field]))
            $this->buildTree($row[$children_field], $display_field, $children_field, $class, $id, $recursionDepth + 1, $maxDepth);

        echo "</li>\n";
    }
    echo "</ul>\n";
}

Вы делаете свою жизнь более сложной, чем это должно быть. The SPL предлагает ряд итераторы для вашего удобства. Перемещение многомерных массивов легко с помощью RecursiveArrayIterator класс. Это не только позволяет обрабатывать любые массивы глубины уровня, но и отслеживать глубину.

пример

$iterator = new RecursiveIteratorIterator(
    new RecursiveArrayIterator($array)
);

for($iterator; $iterator->valid(); $iterator->next())
{
    printf(
        "Key: %s Value: %s Depth: %s\n",
        $iterator->key(),
        $iterator->current(),
        $iterator->getDepth()
    );
}

пример на codepad

как вы можете видеть, есть метод getDepth(), который всегда подскажет вам текущую глубину итерации. Это метод RecursiveIteratorIterator требуется перебирать дочерние элементы в рекурсивных итераторах.

Если вам нужно повлиять на то, что происходит при запуске итерации или при доступе к детям, Посмотрите на мой ответ итерация многомерного массива который показывает обычай RecursiveIteratorIterator это обернет значения многомерного массива в элементы xml и отступит их по глубина текущей итерации (которая должна быть тривиальной для адаптации к элементам ul/li).

также посмотреть статья Википедии об итераторах для общего введения.


public virtual int GetLevelById(int id)
        {
            int i = GetParentById(id);
            if (i == 0)
                return 1;
            else
                return (1 + GetLevelById(i));
        }

общедоступный виртуальный int GetParentById(int t) { var ret = это._список.FirstOrDefault ((f) => f.Id == t); если (рет != недействительный) вернуться в отставке.Атрибутом parentId; возвращают -1; }