Преобразование массива из одного в многомерный на основе значений родительского идентификатора

У меня есть одномерный массив объектов, представляющих многомерные данные:

array( array( "id" => 45, "parent_id" => null ), array( "id" => 200, "parent_id" => 45 ), array( "id" => 345, "parent_id" => 45 ), array( "id" => "355", "parent_id" => 200 ) ); 

Как преобразовать его в многомерный массив:

 array( array( "id" => 45, "parent_id" => null, "children" => array( array( "id" => 200, "parent_id" => 45, "children" => array( "id" => "355", "parent_id" => 200 ) ), array( "id" => 345, "parent_id" => 45 ), ) ), ); 

Следующий пример кода преобразует массив $array в древовидную структуру, которую вы ищете:

 // key the array by id $keyed = array(); foreach($array as &$value) { $keyed[$value['id']] = &$value; } unset($value); $array = $keyed; unset($keyed); // tree it $tree = array(); foreach($array as &$value) { if ($parent = $value['parent_id']) $array[$parent]['children'][] = &$value; else $tree[] = &$value; } unset($value); $array = $tree; unset($tree); var_dump($array); # your result с // key the array by id $keyed = array(); foreach($array as &$value) { $keyed[$value['id']] = &$value; } unset($value); $array = $keyed; unset($keyed); // tree it $tree = array(); foreach($array as &$value) { if ($parent = $value['parent_id']) $array[$parent]['children'][] = &$value; else $tree[] = &$value; } unset($value); $array = $tree; unset($tree); var_dump($array); # your result с // key the array by id $keyed = array(); foreach($array as &$value) { $keyed[$value['id']] = &$value; } unset($value); $array = $keyed; unset($keyed); // tree it $tree = array(); foreach($array as &$value) { if ($parent = $value['parent_id']) $array[$parent]['children'][] = &$value; else $tree[] = &$value; } unset($value); $array = $tree; unset($tree); var_dump($array); # your result с // key the array by id $keyed = array(); foreach($array as &$value) { $keyed[$value['id']] = &$value; } unset($value); $array = $keyed; unset($keyed); // tree it $tree = array(); foreach($array as &$value) { if ($parent = $value['parent_id']) $array[$parent]['children'][] = &$value; else $tree[] = &$value; } unset($value); $array = $tree; unset($tree); var_dump($array); # your result с // key the array by id $keyed = array(); foreach($array as &$value) { $keyed[$value['id']] = &$value; } unset($value); $array = $keyed; unset($keyed); // tree it $tree = array(); foreach($array as &$value) { if ($parent = $value['parent_id']) $array[$parent]['children'][] = &$value; else $tree[] = &$value; } unset($value); $array = $tree; unset($tree); var_dump($array); # your result 

Это не работает, если существует существующий родительский идентификатор, равный 0 . Но можно было легко изменить, чтобы это отразить.

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

Редактировать:

Так, как это работает? Это использует псевдонимы переменных PHP (также называемые ссылками ) и (временные) массивы, которые используются для хранения алиасов для узлов ( $keyed ) и b) для создания нового древовидного ордера ( $tree ).

Не могли бы вы […] объяснить назначение $array = $keyed , $array = $tree и unsets?

Поскольку оба $keyed и $tree содержат ссылки на значения в $array , я сначала копирую эту информацию в $array , например:

 $array = $keyed; 

Поскольку теперь $keyed все еще установлен (и содержит ссылки на те же значения, что и в $array ), $keyed не задан:

 unset($keyed); 

Это отменяет все ссылки в $keyed и гарантирует, что все значения в $array больше не привязаны (пересчет значения уменьшается на единицу).

Если временные массивы не будут отменены после итерации, их ссылки все равно будут существовать. Если вы используете var_dump для $array , вы увидите, что все значения будут иметь & впереди, потому что они по-прежнему ссылаются. unset($keyed) var_dump($array) снова удаляет эти ссылки, var_dump($array) , и вы увидите, что & s ушли.

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

Если вам нужно какое-то упражнение, подумайте о следующем:

Как преобразовать ваш $array from flat to tree с одной итерацией foreach?

Решите сами, когда вы хотите щелкнуть ссылку, содержащую решение .

 function convertArray ($array) { // First, convert the array so that the keys match the ids $reKeyed = array(); foreach ($array as $item) { $reKeyed[(int) $item['id']] = $item; } // Next, use references to associate children with parents foreach ($reKeyed as $id => $item) { if (isset($item['parent_id'], $reKeyed[(int) $item['parent_id']])) { $reKeyed[(int) $item['parent_id']]['children'][] =& $reKeyed[$id]; } } // Finally, go through and remove children from the outer level foreach ($reKeyed as $id => $item) { if (isset($item['parent_id'])) { unset($reKeyed[$id]); } } return $reKeyed; } с function convertArray ($array) { // First, convert the array so that the keys match the ids $reKeyed = array(); foreach ($array as $item) { $reKeyed[(int) $item['id']] = $item; } // Next, use references to associate children with parents foreach ($reKeyed as $id => $item) { if (isset($item['parent_id'], $reKeyed[(int) $item['parent_id']])) { $reKeyed[(int) $item['parent_id']]['children'][] =& $reKeyed[$id]; } } // Finally, go through and remove children from the outer level foreach ($reKeyed as $id => $item) { if (isset($item['parent_id'])) { unset($reKeyed[$id]); } } return $reKeyed; } 

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

ПРИМЕЧАНИЕ. Эта функция основана на parent_id для элементов, у которых ни один родитель не имеет значения NULL или вообще не установлен, поэтому isset() возвращает FALSE в последнем цикле.