SO,преобразование плоского массива дерева с петлей единовременного
Проблема
Предположим, что мы плоский массив со следующей структурой:
$array = [
['level'=>1, 'name' => 'Root #1'],
['level'=>1, 'name' => 'Root #2'],
['level'=>2, 'name' => 'subroot 2-1'],
['level'=>3, 'name' => '__subroot 2-1/1'],
['level'=>2, 'name' => 'subroot 2-2'],
['level'=>1, 'name' => 'Root #3']
];
Этот вопрос - преобразование этого массива поэтому он станет деревом. Субординация определяется только порядком элементов и полем level
. Определим children
как имя измерения для хранения дочерних узлов. Для массива выше, что будет:
array ( array ( 'level' => 1, 'name' => 'Root #1', ), array ( 'level' => 1, 'name' => 'Root #2', 'children' => array ( array ( 'level' => 2, 'name' => 'subroot 2-1', 'children' => array ( array ( 'level' => 3, 'name' => '__subroot 2-1/1', ), ), ), array ( 'level' => 2, 'name' => 'subroot 2-2', ), ), ), array ( 'level' => 1, 'name' => 'Root #3', ), )
немного больше уточнений, если это не очевидно, кто является родителем для тех, кто: следующий код мог легко визуализировать идею:
function visualize($array)
{
foreach($array as $item)
{
echo(str_repeat('-', $item['level']).'['.$item['name'].']'.PHP_EOL);
}
}
visualize($array);
-для массива выше, это:
-[Root #1] -[Root #2] --[subroot 2-1] ---[__subroot 2-1/1] --[subroot 2-2] -[Root #3]
Особенности
Есть некоторые ограничения как для требуемого массива раствора и ввода:
- Входной массив всегда уважительной: это означает, что его структура всегда может быть переработан в древовидной структуре. Нет таких странных вещей, как отрицательные/нечисловые уровни, недействительная структура уровней, e t.c.
- Входной массив может быть огромным, и в настоящее время, максимальный уровень не ограничен
- Решение должны решить вопрос с одного цикла, поэтому мы не можем разделить массив куски, применить рекурсию или скачок в массиве как-то. Просто просто
foreach
(или другой цикл - это не имеет значения), только один раз, каждый элемент один за другим должен обрабатываться.
Мой подход
В настоящее время у меня есть решение с стека. Я работаю со ссылками и поддерживаю текущий элемент стека, на который будет производиться запись на текущем этапе. То есть:
function getTree(&$array)
{
$level = 0;
$tree = [];
$stack = [&$tree];
foreach($array as $item)
{
if($item['level']>$level) //expand stack for new items
{
//if there are child elements, add last to stack:
$top = key($stack);
if(count($stack[$top]))
{
end($stack[$top]);
$stack[] = &$stack[$top][key($stack[$top])];
}
//add ['children'] dim to top stack element
end($stack);
$top = key($stack);
$stack[$top]['children'] = [];
$stack[] = &$stack[$top]['children'];
}
while($item['level']<$level--) //pop till certain level
{
//two times: one for last pointer, one for ['children'] dim
array_pop($stack);
array_pop($stack);
}
//add item to stack top:
end($stack);
$stack[key($stack)][] = $item;
$level = $item['level'];
}
return $tree;
}
-since это достаточно долго, я создал sample использование & выхода.
Вопрос
Как вы можете видеть, что мое решение довольно долго, и она опирается на ссылки & массива внутренний указатель обработки (такие вещи, как end()
), так вопрос является:
Могут ли быть другие - более короткие и четкие пути решения этой проблемы?Похоже, какой-то стандартный вопрос, но я не нашел соответствующее решение (есть один аналогичный question - но О.П. имеет точное parent_id
подчиненность, пока я не)
Спасибо вам. Даже если это не соответствует моим требованиям - это все равно интересно. Тем не менее, я выполнил некоторые меры после очистки кода в своей функции - и оба они имеют одинаковое время для счетчиков и массивов «1E6», которые указаны в вопросе ('70 секунд» моей версии против '74,5 сек'с ваш). Я также тестировал на простом массиве (все уровни - '1') и получили аналогичные результаты (' 34 сек' мой против '39 сек'с ваш). Это как ожидалось, потому что обе функции имеют такую же оценку «большой-O» –