рекурсивный обход дерева - как отслеживать уровень рекурсии?
Я в основном пытаюсь построить вложенный список 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()
);
}
как вы можете видеть, есть метод 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; }