Я в основном пытаюсь построить 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 ) )
Я отметил это как домашнюю работу, потому что я хотел бы использовать это как возможность улучшить мое (плохое) понимание рекурсии, поэтому, я буду благодарен за ответы, которые помогут мне решить проблему, а не предоставить полный рабочий пример: )
Quick'n'dirty подход (см. Блок «spoiler» ниже для реализации):
Добавьте дополнительную переменную $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"; }
Вы делаете свою жизнь труднее, чем нужно. 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()
который всегда будет 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)); }
public virtual int GetParentById (int t) {var ret = this._list.FirstOrDefault ((f) => f.Id == t); if (ret! = null) return ret.ParentId; return -1; }