2011-12-21 105 views
63

我正在尋找編寫一個函數,該函數需要一個頁面/類別的數組(從平面數據庫結果),並基於父ID生成一個嵌套的頁面/類別項目數組。我想遞歸地做到這一點,以便可以完成任何級別的嵌套。從數據庫結果生成多維數組遞歸函數

例如:我取一個查詢中的所有網頁,這是什麼樣的數據庫表看起來像

+-------+---------------+---------------------------+ 
| id | parent_id |   title   | 
+-------+---------------+---------------------------+ 
| 1 |  0  | Parent Page    | 
| 2 |  1  | Sub Page    | 
| 3 |  2  | Sub Sub Page   | 
| 4 |  0  | Another Parent Page  | 
+-------+---------------+---------------------------+ 

這是我想與處理在結束了陣列我的視圖文件:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [parent_id] => 0 
      [title] => Parent Page 
      [children] => Array 
         (
          [0] => Array 
           (
            [id] => 2 
            [parent_id] => 1 
            [title] => Sub Page 
            [children] => Array 
               (
                [0] => Array 
                 (
                  [id] => 3 
                  [parent_id] => 1 
                  [title] => Sub Sub Page 
                 ) 
               ) 
           ) 
         ) 
     ) 
    [1] => Array 
     (
      [id] => 4 
      [parent_id] => 0 
      [title] => Another Parent Page 
     ) 
) 

我看,並試圖幾乎每一個解決方案,我已經遇到(還有這裏的很多人對堆棧溢出,但有沒有運氣得到的東西很通用,將兩個工作網頁和類別。

這是我收到的最接近的,但它不起作用,因爲我將孩子分配給一級父母。

function page_walk($array, $parent_id = FALSE) 
{ 
    $organized_pages = array(); 

    $children = array(); 

    foreach($array as $index => $page) 
    { 
     if ($page['parent_id'] == 0) // No, just spit it out and you're done 
     { 
      $organized_pages[$index] = $page; 
     } 
     else // If it does, 
     {  
      $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id); 
     } 
    } 

    return $organized_pages; 
} 

function page_list($array) 
{  
    $fakepages = array(); 
    $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page'); 
    $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page'); 
    $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page'); 
    $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page'); 

    $pages = $this->page_walk($fakepages, 0); 

    print_r($pages); 
} 
+1

你就不能與所有parent_ids的陣列和另一個陣列工作你的網頁? – djot 2011-12-21 09:17:50

回答

169

一些很簡單的,通用的建樹:

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

    return $branch; 
} 

$tree = buildTree($rows); 

的算法很簡單:

  1. 採取的所有元素的數組和當前父的ID(最初0/nothing/null/whatever)。
  2. 循環遍歷所有元素。
  3. 如果元素的parent_id與您在1中獲得的當前父ID相匹配,則該元素是父級的子元素。把它放在當前孩子的名單中(這裏是:$branch)。
  4. 用3中剛剛標識的元素的id遞歸地調用該函數,即查找該元素的所有子元素,並將它們添加爲children元素。
  5. 返回找到的孩子的列表。

換句話說,此函數的一個執行返回給定父id的子元素列表。用buildTree($myArray, 1)來調用它,它將返回一個包含父id的元素列表。最初調用這個函數時,父id爲0,所以返回沒有父id的元素,它們是根節點。該函數遞歸地調用它自己以查找兒童的孩子。

+0

這個效果很好。我可以看到我的版本邏輯中出錯的地方。方式來搖滾它!我並不需要經常在項目中使用遞歸,所以這肯定有很大的幫助。 – 2011-12-21 09:33:56

+1

很高興幫助。需要注意的是:由於它總是傳遞整個'$ elements'數組,因此效率低下。對於幾乎不重要的小型數組,但對於大型數據集,在傳遞它之前,您需要從中刪除已匹配的元素。儘管如此,這變得有點麻煩,所以爲了更容易理解,我將它留下了。 :) – deceze 2011-12-21 09:36:14

+5

@deceze我想看到混亂的版本。提前致謝! – 2013-01-24 13:37:12

0

可以使用PHP將mysql結果導入數組,然後使用它。

$categoryArr = Array(); 
while($categoryRow = mysql_fetch_array($category_query_result)){ 
    $categoryArr[] = array('parentid'=>$categoryRow['parent_id'], 
      'id'=>$categoryRow['id']); 
    } 
9

我知道這個問題很舊,但我面臨着一個非常類似的問題 - 除了數據量非常大。經過一番努力之後,我設法通過結果集的一遍構建樹 - 使用引用。這段代碼並不漂亮,但它的工作原理和工作速度非常快。它的非遞歸 - 也就是說,只有一個在結果集通,然後一個array_filter底:

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD); 
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id"); 
$elems = array(); 

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) { 
    $row['children'] = array(); 
    $vn = "row" . $row['n_id']; 
    ${$vn} = $row; 
    if(!is_null($row['n_parent_id'])) { 
     $vp = "parent" . $row['n_parent_id']; 
     if(isset($data[$row['n_parent_id']])) { 
      ${$vp} = $data[$row['n_parent_id']]; 
     } 
     else { 
      ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array()); 
      $data[$row['n_parent_id']] = &${$vp}; 
     } 
     ${$vp}['children'][] = &${$vn}; 
     $data[$row['n_parent_id']] = ${$vp}; 
    } 
    $data[$row['n_id']] = &${$vn}; 
} 
$dbs->closeCursor(); 

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); }); 
print_r($result); 

當這個數據執行:

mysql> select * from test_table; 
+------+-------------+ 
| n_id | n_parent_id | 
+------+-------------+ 
| 1 |  NULL | 
| 2 |  NULL | 
| 3 |   1 | 
| 4 |   1 | 
| 5 |   2 | 
| 6 |   2 | 
| 7 |   5 | 
| 8 |   5 | 
+------+-------------+ 

最後print_r產生這樣的輸出:

Array 
(
    [1] => Array 
     (
      [n_id] => 1 
      [n_parent_id] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [n_id] => 3 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

        [4] => Array 
         (
          [n_id] => 4 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [n_id] => 2 
      [n_parent_id] => 
      [children] => Array 
       (
        [5] => Array 
         (
          [n_id] => 5 
          [n_parent_id] => 2 
          [children] => Array 
           (
            [7] => Array 
             (
              [n_id] => 7 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

            [8] => Array 
             (
              [n_id] => 8 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [6] => Array 
         (
          [n_id] => 6 
          [n_parent_id] => 2 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

) 

這正是我在找的東西。

+0

你真了不起。這對我來說非常合適。我從過去兩天開始搜索這個。這個答案應該被標記爲最佳答案。 – Swadesh 2014-06-21 15:40:24

+0

雖然解決方案很聰明,但是這個代碼有bug,它給了我不同情況下的不同結果 – Mohammadhzp 2015-08-14 02:47:57

+0

@Mohammadhzp我在去年的生產中一直使用這個解決方案,並且沒有問題。如果你的數據是不同的,你會得到不同的結果:) – 2015-08-14 13:42:34

0

對於大型陣列:

通過引用傳遞數組。

function buildTree(&$elements, $parentId = 0) { //pass main array by reference 
    $branch = array(); 

    foreach ($elements as $key => $element) { 
     if ($element['parent_id'] == $parentId) { 

      $element['children'] = buildTree($elements, $element['id']); 

      $branch[] = $element;     
     } 
    } 
    return $branch; 
} 

$tree = buildTree($rows); 

如果你傳遞數組作爲參考,然後同樣的陣列將在每一個遞歸函數的使用不需要在年底合併父子陣列