web-dev-qa-db-ger.com

Array-Baum aus Array-Liste erstellen

ich habe eine Liste wie diese:

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'),
)

aber viel größer, also brauche ich einen effizienten Weg, um daraus eine baumähnliche Struktur zu machen:

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',
    )
  )
)

ich kann keine verschachtelten Mengen oder ähnliches verwenden, da ich der Datenbank linke und rechte Werte hinzufügen kann. irgendwelche Ideen?

43
Thunderstriker

so habe ich es gelöst:

$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;
}
55
Thunderstriker

kleine Korrektur, wenn Sie mehr als 1 parentid [0] -Element benötigen :)

$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;
}
40
arthur

Noch eine Überarbeitung von Thunderstrikers Variante - die gesamte Logik in einer Funktion:

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);

Spielplatz: https://www.tehplayground.com/5V8QSqnmFJ2wcIoj

12
Vasily

Hier ist meine Adaption von arthurs Überarbeitung :

/* 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]);
}

Verwenden:

$tree = createTree($flat);
9

Ich habe eine ungewöhnliche ('während-basiert' statt rekursiv), aber mehrdimensional Sortierfunktion erstellt, die das Array durchläuft, bis keine Waisen mehr vorhanden sind. Hier die Funktion:

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] );
            }
        }
    }
}

Empfehlung: Der Schlüssel jedes Elements des Arrays muss die ID des Elements selbst sein. Beispiel:

$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' ),
);

sage: Die Funktion benötigt $ a (das Array), $ parent_key (den Namen der Spalte, in der die ID des Vaters gespeichert ist), $ children_key (den Namen der Spalte, in der die Kinder gespeichert werden) Bewegung). Es wird nichts zurückgegeben (das Array wird durch Verweis geändert). Beispiel:

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

Gibt es einen Grund, warum diese Drei-Pass-Methode nicht funktioniert? Ich habe keine Tests durchgeführt, um die Geschwindigkeit mit einigen der rekursiven Lösungen zu vergleichen, aber es schien einfacher zu sein. Wenn Ihr ursprüngliches Array bereits mit den IDs assoziiert ist, die der Schlüssel sind, können Sie das erste foreach() überspringen.

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;
}
1
psyon

Eine Möglichkeit, dies zu tun, besteht in einer rekursiven Funktion, die zuerst alle unteren Werte der Liste findet und sie einem neuen Array hinzufügt. Dann verwenden Sie für jede neue ID dieselbe Funktion für diese ID, nehmen das zurückgegebene Array und fügen es in das neue untergeordnete Array des Elements ein. Schließlich geben Sie Ihr neues Array zurück.

Ich werde nicht die ganze Arbeit für Sie erledigen, aber die Parameter der Funktion sehen ungefähr so ​​aus:

funktion recursiveChildren ($ items_array, $ parent_id = 0)

Im Wesentlichen findet es alle mit Eltern von 0, dann findet es für jeden von denen alle mit dieser ID als Eltern und für jeden von denen .. so weiter.

Das Endergebnis sollte das sein, wonach Sie suchen.

1
DampeS8N
//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>";
1
Pham