2012-01-12 13 views
24

He buscado en Internet y todavía no he encontrado lo que estoy buscando. Tengo una matriz plana con cada elemento que contiene un 'id' y un 'parent_id'. Cada elemento solo tendrá UN padre, pero puede tener varios hijos. Si parent_id = 0, se considera un elemento de nivel de raíz. Estoy tratando de obtener mi matriz plana en un árbol. Las otras muestras que he encontrado solo copian el elemento al padre, pero el original aún existe.Construir un árbol desde una matriz plana en PHP

EDITAR

Cada elemento de la matriz de partida se lee de un archivo XML independiente. El archivo en sí tendrá '0' como el valor para parent_id si no tiene un padre. Las claves son en realidad cadenas.

Disculpa la confusión anterior. Espero que esto es más claro:

/EDIT

Mi matriz de partida:

 
Array 
(
    [_319_] => Array 
     (
      [id] => 0 
      [parent_id] => 0 
     ) 

    [_320_] => Array 
     (
      [id] => _320_ 
      [parent_id] => 0 
     ) 

    [_321_] => Array 
     (
      [id] => _321_ 
      [parent_id] => _320_ 
     ) 

    [_322_] => Array 
     (
      [id] => _322_ 
      [parent_id] => _321_ 
     ) 

    [_323_] => Array 
     (
      [id] => _323_ 
      [parent_id] => 0 
     ) 

    [_324_] => Array 
     (
      [id] => _324_ 
      [parent_id] => _323_ 
     ) 

    [_325_] => Array 
     (
      [id] => _325_ 
      [parent_id] => _320_ 
     ) 
)

La matriz resultante después se hace el árbol:

 
Array 
(
    [_319_] => Array 
     (
      [id] => _319_ 
      [parent_id] => 0 
     ) 

    [_320_] => Array 
     (
      [id] => _320_ 
      [parent_id] => 0 
      [children] => Array 
       (
        [_321_] => Array 
         (
          [id] => _321_ 
          [parent_id] => _320_ 
          [children] => Array 
           (
            [_322_] => Array 
             (
              [id] => _322_ 
              [parent_id] => _321_ 
             ) 
           ) 
         ) 
        [_325_] => Array 
         (
          [id] => _325_ 
          [parent_id] => _320_ 
         ) 
       ) 
    [_323_] => Array 
     (
      [id] => _323_ 
      [parent_id] => 0 
      [children] => Array 
       (
        [_324_] => Array 
         (
          [id] => _324_ 
          [parent_id] => _323_ 
         ) 
       ) 
     ) 

Cualquier ayuda/orientación es ¡apreciado enormemente!

Parte del código que tengo hasta ahora:

 

     function buildTree(array &$elements, $parentId = 0) { 
     $branch = array(); 

     foreach ($elements as $element) { 
      if ($element['parent_id'] == $parentId) { 
       $children = $this->buildTree($elements, $element['id']); 
       if ($children) { 
        $element['children'] = $children; 
       } 
       $branch[] = $element; 
      } 
     } 

     return $branch; 
    } 

+1

I'm confused. ¿Simplemente nos pides que escribamos el código que toma tu primer arreglo y escupemos lo que tienes en el segundo conjunto? – MetalFrog

+0

Sí ... ¿cuál es la pregunta aquí? –

+0

En resumen, supongo que sí. He visto varios otros ejemplos aquí en stackoverflow y en otros blogs/foros. Pero cuando los he probado, no funcionan. – DSkinner

Respuesta

38

se le olvidó la unset() allí bro.

function buildTree(array &$elements, $parentId = 0) { 
    $branch = array(); 

    foreach ($elements as $element) { 
     if ($element['parent_id'] == $parentId) { 
      $children = buildTree($elements, $element['id']); 
      if ($children) { 
       $element['children'] = $children; 
      } 
      $branch[$element['id']] = $element; 
      unset($elements[$element['id']]); 
     } 
    } 
    return $branch; 
} 
+3

Esta solución podría fallar en la construcción correcta del árbol en ciertas circunstancias (p.$ arr = array (array ('id' => 1, 'parentid' => 0), array ('id' => 10, 'parentid' => 2), array ('id' => 2, 'parentid '=> 0), array (' id '=> 3,' parentid '=> 10), array (' id '=> 4,' parentid '=> 0), array (' id '=> 11,' parentid '=> 1), array (' id '=> 5,' parentid '=> 0), array (' id '=> 6,' parentid '=> 1), array (' id '=> 8, 'parentid' => 11), array ('id' => 9, 'parentid' => 0), array ('id' => 7, 'parentid' => 0),);) Sugeriría: http://stackoverflow.com/questions/4196157/create-array-tree-from-array-list (solución modificada de Arthur) – danicotra

+1

No se guardan los primeros padres sin hijos. – mrded

+0

@Freedom_Ben gracias, intento :) – n0nag0n

3

puedo ver la lógica, a excepción de esto en el resultado:

Array 
(
    [0] => Array 
     (
      [id] => 0 
      [parent_id] => 0 
     ) 

    [1] => Array 
     (
      [id] => 1 
      [parent_id] => 0 
     ) 

en mi humilde opinión, es parent_id = O, no debería [1 ] ser un niño de [0] aquí?

De todos modos, las referencias al rescate:

$tree = array(); 
foreach($inputarray as $item){ 
    if(!isset($tree[$item['id']])) $tree[$item['id']] = array(); 
    $tree[$item['id']] = array_merge($tree[$item['id']],$item); 
    if(!isset($tree[$item['parent_id']])) $tree[$item['parent_id']] = array(); 
    if(!isset($tree[$item['parent_id']]['children'])) $tree[$item['parent_id']]['children'] = array(); 
    $tree[$item['parent_id']]['children'][] = &$tree[$item['id']]; 
} 
$result = $tree[0]['children']; 
unset($tree); 
print_r($result); 

porque usted ha abusado tanto 0 como un número 'mágico' como root, y un ID existente, ahora tenemos la recursividad en el id = 0 rama. Agregar if($item['parent_id']!=$item['id']) antes de $tree[$item['parent_id']]['children'][] = &$tree[$item['id']]; podría evitar eso, pero no es bonito.

+0

+1 porque la recusión ha permitido agotar el tamaño de la memoria en mi caso. En mi caso, había 54 objetos y esto fue suficiente para cumplir con mi memoria. – bumerang

2

Es posible construir la matriz de origen ligeramente diferente que puede utilizar esta función (parent_id, id, title):

$q = mysql_query("SELECT id, parent_id, name FROM categories"); 
while ($r = mysql_fetch_row($q)) { 
    $names[$r[0]] = $r[2]; 
    $children[$r[0]][] = $r[1]; 
} 

function render_select($root=0, $level=-1) { 
    global $names, $children; 
    if ($root != 0) 
    echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>'; 
    foreach ($children[$root] as $child) 
    render_select($child, $level+1); 
} 

echo '<select>'; 
render_select(); 
echo '</select>'; 
  1. More efficient hierarchy system
0

usted quiere estar mirando el almacenamiento y cargar datos jerárquicos en MySQL ya que esto debería resolver algunos problemas. Supongo que la primera matriz representa datos tomados directamente de la base de datos.

Parece que está intentando usar el modelo de adyacencia para organizar sus datos en la estructura jerárquica. También hay otras formas de lograr esto utilizando el anidamiento. Si no está tomando estos datos de una base de datos, esto puede no ser tan útil.

Este enlace le ayudará a cabo: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

+0

Aunque ilustra correctamente el uso del Modelo de adyacencia como el conjunto anidado, en la práctica (al menos, en mi experiencia), el modelo de conjunto anidado es muy costoso en mutaciones (¡en promedio, la mitad de su tabla debe actualizarse!) para cualquier información práctica. Si los datos son relativamente obsoletos (es decir, rara vez cambian), entonces es viable, pero por lo general, este no es el caso. – Wrikken

+0

@Wrikken Sí, depende de cómo se usen/actualicen los datos. Para las categorías está bien, pero para los datos con muchas modificaciones no es viable en absoluto. Olvidé mencionarlo, gracias :) –

5

Esto funciona para mí:

$index=array(); 
$tree=array(); 
foreach ($ori as $key=>$var) { 
    $var=array_shift($ori); 
    if ($var['id']==0) $var['id']=$key; 
    if ((string)$var['parent_id']==='0') { 
    $tree[$key]=$var; 
    $index[$key]=&$tree[$key]; 
    } else if (isset($index[$var['parent_id']])) { 
    if (!isset($index[$var['parent_id']]['children'])) $index[$var['parent_id']]['children']=array(); 
    $index[$var['parent_id']]['children'][$key]=$var; 
    $index[$key]=&$index[$var['parent_id']]['children'][$key]; 
    } else { 
    array_push($ori,$var); 
    } 
} 
unset($index); 
print_r($tree); 
+0

Me encanta el uso del índice. Brillante. –

0

Aquí está mi solución, funciona idealmente, si suponemos que el parent_id nivel superior = 0:

function MakeTree($arr){ 
    $parents_arr=array(); 
    foreach ($arr as $key => $value) { 
     $parents_arr[$value['pid']][$value['id']]=$value; 
    } 
    $tree=$parents_arr['0']; 
    $this->createTree($tree, $parents_arr); 
    return $tree; 
} 
function createTree(&$tree, $parents_arr){ 
    foreach ($tree as $key => $value) { 
     if(!isset($value['children'])) { 
      $tree[$key]['children']=array(); 
     } 
     if(array_key_exists($key, $parents_arr)){ 
      $tree[$key]['children']=$parents_arr[$key]; 
      $this->createTree($tree[$key]['children'], $parents_arr); 
     } 
    } 
} 
2

Aunque esta es una vieja pregunta, voy a postear mi respuesta aquí:

/* assuming top level pid = 0 */ 
$rows = array (
    array ('id' => 1, 'pid' => 0), 
    /* ... */ 
); 

/* make id become array key */ 
$rows = array_column ($rows, null, 'id'); 

foreach ($rows as $key => $val) { 
    if ($val ['pid']) { 
     if (isset ($rows [$val ['pid']])) { 
      $rows [$val ['pid']]['children'][] = &$rows [$key]; 
     } 
    } 
} 

foreach ($rows as $key => $val) { 
    if ($val ['pid']) unset ($rows [$key]); 
} 

array_column es PHP 5.5 pero puede hacer uno fácilmente.

22

La solución de ImmortalFirefly funciona, sin embargo, como señala, no salva a los primeros padres sin hijos. He editado la función para solucionar este problema:

function buildTree(array &$elements, $parentId = 0) { 

    $branch = array(); 

    foreach ($elements as &$element) { 

     if ($element['parent_id'] == $parentId) { 
      $children = buildTree($elements, $element['id']); 
      if ($children) { 
       $element['children'] = $children; 
      } 
      $branch[$element['id']] = $element; 
      unset($element); 
     } 
    } 
    return $branch; 
} 
+0

Acabas de guardar mi 6. Mi jefe estaba por golpearme hasta la muerte. Uf. Muchas gracias hombre. –

+0

¡Gracias! ¡Justo lo que necesitaba! – maxpower9000

0

Esta es mi solución, copio y optimizo otras soluciones.

function buildTree(array &$elements, $parentId = 0) { 
    $branch = array(); 
    foreach ($elements as $key => $element) { 
     if ($element['parent_id'] == $parentId) { 
      $children = $this->buildTree($elements, $key); 
      if ($children) { 
       $element['children'] = $children; 
      } 
      $branch[$key] = $element; 
      unset($elements[$key]); 
     } 
    } 
    return $branch; 
} 
0

Limpio, corto y sin lastre. Matriz de matrices en árbol:

class Mother { 
    private $root; 
    public function treeInit($array) 
    { 
     $this->root = new Child(); 
     foreach($array as $value){ 
      $this->root->treeClimb(array_reverse($value)); 
     } 
     return $this->root; 
    } 
} 

class Child { 
    private $children = []; 
    public function treeClimb($arr) 
    { 
     if(count($arr) > 0) { 
      $childTmp = array_pop($arr); 
      if(!key_exists($childTmp,$this->children)) 
      { 
       $this->children[$childTmp] = new Child(); 
      } 
     $this->children[$childTmp]->treeClimb($arr); 
     } 
    } 
} 

$array = array(array('obst','banae','krumm','gelb'), 
        array('obst','beere','him'), 
        array('obst','beere','brom'), 
        array('obst','banae','gerade'), 
        array('veg','carot','gerade')); 

$obj = new Mother(); 
var_dump($obj->treeInit($array)); 
Cuestiones relacionadas