создать массив из списка массивов

У меня есть список:

array( array(id=>100, parentid=>0, name=>'a'), array(id=>101, parentid=>100, name=>'a'), array(id=>102, parentid=>101, name=>'a'), array(id=>103, parentid=>101, name=>'a'), ) 

но путь больше, поэтому мне нужен эффективный способ превратить это в дерево вроде такой структуры:

 array( id=>100, parentid=>0, name=>'a', children=>array( id=>101, parentid=>100, name=>'a', children=>array( id=>102, parentid=>101, name=>'a', id=>103, parentid=>101, name=>'a', ) ) ) 

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

Solutions Collecting From Web of "создать массив из списка массивов"

oke вот как я решил это:

 $arr = array( array('id'=>100, 'parentid'=>0, 'name'=>'a'), array('id'=>101, 'parentid'=>100, 'name'=>'a'), array('id'=>102, 'parentid'=>101, 'name'=>'a'), array('id'=>103, 'parentid'=>101, 'name'=>'a'), ); $new = array(); foreach ($arr as $a){ $new[$a['parentid']][] = $a; } $tree = createTree($new, array($arr[0])); print_r($tree); function createTree(&$list, $parent){ $tree = array(); foreach ($parent as $k=>$l){ if(isset($list[$l['id']])){ $l['children'] = createTree($list, $list[$l['id']]); } $tree[] = $l; } return $tree; } 

маленькое исправление, если вам нужно больше 1 родительского элемента [0] 🙂

 $arr = array( array('id'=>100, 'parentid'=>0, 'name'=>'a'), array('id'=>101, 'parentid'=>100, 'name'=>'a'), array('id'=>102, 'parentid'=>101, 'name'=>'a'), array('id'=>103, 'parentid'=>101, 'name'=>'a'), ); $new = array(); foreach ($arr as $a){ $new[$a['parentid']][] = $a; } $tree = createTree($new, $new[0]); // changed print_r($tree); function createTree(&$list, $parent){ $tree = array(); foreach ($parent as $k=>$l){ if(isset($list[$l['id']])){ $l['children'] = createTree($list, $list[$l['id']]); } $tree[] = $l; } return $tree; } 

Вот моя адаптация от переделки Артура :

 /* Recursive branch extrusion */ function createBranch(&$parents, $children) { $tree = array(); foreach ($children as $child) { if (isset($parents[$child['id']])) { $child['children'] = $this->createBranch($parents, $parents[$child['id']]); } $tree[] = $child; } return $tree; } /* Initialization */ function createTree($flat, $root = 0) { $parents = array(); foreach ($flat as $a) { $parents[$a['parent']][] = $a; } return $this->createBranch($parents, $parents[$root]); } 

Использование:

 $tree = createTree($flat); 

Еще одна доработка варианта Thunderstriker – вся логика в одной функции:

 function buildTree($flat, $pidKey, $idKey = null) { $grouped = array(); foreach ($flat as $sub){ $grouped[$sub[$pidKey]][] = $sub; } $fnBuilder = function($siblings) use (&$fnBuilder, $grouped, $idKey) { foreach ($siblings as $k => $sibling) { $id = $sibling[$idKey]; if(isset($grouped[$id])) { $sibling['children'] = $fnBuilder($grouped[$id]); } $siblings[$k] = $sibling; } return $siblings; }; $tree = $fnBuilder($grouped[0]); return $tree; } // Example: $flat = [ ['id'=>100, 'parentID'=>0, 'name'=>'a'], ['id'=>101, 'parentID'=>100, 'name'=>'a'], ['id'=>102, 'parentID'=>101, 'name'=>'a'], ['id'=>103, 'parentID'=>101, 'name'=>'a'], ]; $tree = buildTree($flat, 'parentID', 'id'); print_r($tree); 

Игровая площадка: https://www.tehplayground.com/5V8QSqnmFJ2wcIoj

Я создал необычную (вместо «рекурсивной»), но многомерную функцию сортировки, которая перемещает массив до тех пор, пока нет сирот. Здесь функция:

 function treeze( &$a, $parent_key, $children_key ) { $orphans = true; $i; while( $orphans ) { $orphans = false; foreach( $a as $k=>$v ) { // is there $a[$k] sons? $sons = false; foreach( $a as $x=>$y ) if( isset($y[$parent_key]) and $y[$parent_key]!=false and $y[$parent_key]==$k ) { $sons=true; $orphans=true; break; } // $a[$k] is a son, without children, so i can move it if( !$sons and isset($v[$parent_key]) and $v[$parent_key]!=false ) { $a[$v[$parent_key]][$children_key][$k] = $v; unset( $a[$k] ); } } } } 

Рекомендация: ключ каждого элемента массива должен быть идентификатором самого элемента. Пример:

 $ARRAY = array( 1 => array( 'label' => "A" ), 2 => array( 'label' => "B" ), 3 => array( 'label' => "C" ), 4 => array( 'label' => "D" ), 5 => array( 'label' => "one", 'father' => '1' ), 6 => array( 'label' => "two", 'father' => '1' ), 7 => array( 'label' => "three", 'father' => '1' ), 8 => array( 'label' => "node 1", 'father' => '2' ), 9 => array( 'label' => "node 2", 'father' => '2' ), 10 => array( 'label' => "node 3", 'father' => '2' ), 11 => array( 'label' => "I", 'father' => '9' ), 12 => array( 'label' => "II", 'father' => '9' ), 13 => array( 'label' => "III", 'father' => '9' ), 14 => array( 'label' => "IV", 'father' => '9' ), 15 => array( 'label' => "V", 'father' => '9' ), ); 

Использование: функции требуется $ a (массив), $ parent_key (имя столбца, где сохраняется идентификатор отца), $ children_key (имя столбца, в котором будут перемещаться дети). Он ничего не возвращает (массив изменяется по ссылке). Пример:

 treeze( $ARRAY, 'father', 'children' ); echo "<pre>"; print_r( $ARRAY ); 

Один из способов сделать это – рекурсивная функция, которая сначала находит все нижние значения списка, добавляя их в новый массив. Затем для каждого нового идентификатора вы используете ту же функцию для этого id, беря возвращаемый массив и набивая его в новый дочерний массив этого элемента. Наконец, вы возвращаете новый массив.

Я не буду делать всю работу за вас, но параметры функции будут выглядеть примерно так:

функция recursiveChildren ($ items_array, $ parent_id = 0)

По сути, он найдет все те, у которых есть родительский элемент 0, затем для каждого из них он найдет все те, у которых этот id будет родителем, и для каждого из них .. так далее.

Конечный результат должен быть тем, что вы ищете.

 //if order by parentid, id $arr = array( array('id'=>100, 'parentid'=>0, 'name'=>'a'), array('id'=>101, 'parentid'=>100, 'name'=>'a'), array('id'=>102, 'parentid'=>101, 'name'=>'a'), array('id'=>103, 'parentid'=>101, 'name'=>'a'), ); $arr_tree = array(); $arr_tmp = array(); foreach ($arr as $item) { $parentid = $item['parentid']; $id = $item['id']; if ($parentid == 0) { $arr_tree[$id] = $item; $arr_tmp[$id] = &$arr_tree[$id]; } else { if (!empty($arr_tmp[$parentid])) { $arr_tmp[$parentid]['children'][$id] = $item; $arr_tmp[$id] = &$arr_tmp[$parentid]['children'][$id]; } } } unset($arr_tmp); echo '<pre>'; print_r($arr_tree); echo "</pre>"; с //if order by parentid, id $arr = array( array('id'=>100, 'parentid'=>0, 'name'=>'a'), array('id'=>101, 'parentid'=>100, 'name'=>'a'), array('id'=>102, 'parentid'=>101, 'name'=>'a'), array('id'=>103, 'parentid'=>101, 'name'=>'a'), ); $arr_tree = array(); $arr_tmp = array(); foreach ($arr as $item) { $parentid = $item['parentid']; $id = $item['id']; if ($parentid == 0) { $arr_tree[$id] = $item; $arr_tmp[$id] = &$arr_tree[$id]; } else { if (!empty($arr_tmp[$parentid])) { $arr_tmp[$parentid]['children'][$id] = $item; $arr_tmp[$id] = &$arr_tmp[$parentid]['children'][$id]; } } } unset($arr_tmp); echo '<pre>'; print_r($arr_tree); echo "</pre>"; 

Есть ли причина, почему этот метод с тремя проходами не будет работать? Я не проводил никаких тестов, чтобы сравнить скорость с некоторыми рекурсивными решениями, но это казалось более прочным. Если ваш начальный массив уже ассоциативен с идентификаторами, являющимися ключом, то вы можете пропустить первый foreach ().

 function array_tree(&$array) { $tree = array(); // Create an associative array with each key being the ID of the item foreach($array as $k => &$v) $tree[$v['id']] = &$v; // Loop over the array and add each child to their parent foreach($tree as $k => &$v) { if(!$v['parent']) continue; $tree[$v['parent']]['children'][] = &$v; } // Loop over the array again and remove any items that don't have a parent of 0; foreach($tree as $k => &$v) { if(!$v['parent']) continue; unset($tree[$k]); } return $tree; } с function array_tree(&$array) { $tree = array(); // Create an associative array with each key being the ID of the item foreach($array as $k => &$v) $tree[$v['id']] = &$v; // Loop over the array and add each child to their parent foreach($tree as $k => &$v) { if(!$v['parent']) continue; $tree[$v['parent']]['children'][] = &$v; } // Loop over the array again and remove any items that don't have a parent of 0; foreach($tree as $k => &$v) { if(!$v['parent']) continue; unset($tree[$k]); } return $tree; }