2011-09-13 37 views
1

我有對象與對象JavaScript陣列看起來像這樣:構建從平坦對象陣列的對象圖以JavaScript

的itemId
名稱
parentItemId沒有父< ==頂層項目有空值

我想要構建一個父項包含子項的數組,並且這些子項具有子項數組(如果適用)的圖。

什麼是一個很好的方式去做這件事?

+0

你可以嘗試像http://www.jqplot.com/ –

+0

插件嗨約瑟夫,有趣的項目!但我不是指圖表/圖表......只是一個嵌套對象「圖」.. –

回答

3
function objectGraph(items) 
{ 
    var items_by_id = {}; 
    var roots = []; 
    var i; 

    // Build an id->object mapping, so we don't have to go hunting for parents 
    for (i = 0; i < items.length; ++i) { 
     items_by_id[items[i].itemId] = items[i]; 
     items[i].children = []; 
    } 

    for (i = 0; i < items.length; ++i) { 
     var parentId = items[i].parentItemId; 
     // If parentId is null, this is a root; otherwise, it's parentId's kid 
     var nodes = (parentId === null) ? roots : items_by_id[parentId].children; 
     nodes.push(items[i]); 
    } 
    return roots; 
} 

注意,這個代碼給每個節點的children財產,這是空的,如果一個節點沒有孩子。我個人覺得它比每個節點更簡單和更一致,可能 - 也許 - 沒有孩子;您可以循環使用children而不用擔心它是否存在。葉節點將有children.length == 0

如果您可以保證您只有一個根,您可以return roots[0];而不是返回數組。

+0

所以,而不是做如果(孩子)你如果(children.length == 0)...嗯我想這有它的用途,如果你想毯子迭代項目,然後決定是否它的葉子。我通常是這樣做的。 – James

+0

而不是做'if(children)',你會做'if(children.length)'。無論哪種方式都可以正常工作(檢查列表是否存在以及它是否包含任何內容),但後者意味着更少的特殊框架,並且不必擔心是否會出現未定義對象錯誤。如果你願意,你可以像對待任何其他對象一樣處理一個葉節點,並迭代它的孩子;你不會有任何孩子去做任何事情。 :)請注意,DOM的工作原理類似;每個節點都有一個兒童列表,即使該列表是空的。 – cHao

+0

足夠真實,並且firefox(可能所有瀏覽器)都會爲孩子返回0。一個空的DOM元素的長度。我的解決方案也做同樣的事情哈哈。 – James

0

這有點粗糙,但如果我正確收集您的問題,它應該做的工作。它應該返回一個頂級對象的數組,其正確組織他們的子級。

作爲一個說明,這將適用於N級兒童對象,而不僅僅是一個級別。

var finalArray = []; 
var YOUR_RAW_ARRAY = []; 

var buildObjectGraph = function(inputArray){ 
    var i = 0, len = inputArray.length; 
    var returnVal = []; 

    for(;i<len;i++){ 
     if(inputArray[i].parentItemId === null){ 
      findChildren(inputArray[i], inputArray); 
      returnVal.push(inputArray[i]); 
     } 
    } 

    var findChildren = function(root){ 
     var i = 0, i2 = 0, len = rawDataArray.length, len2 = 0; 

     for(;i<len;i++){ 
      if(inputArray[i].parentItemId === root.itemId){ 
       if(root.children){ 
        root.children.push(inputArray[i]); 
       }else{ 
        root.children = []; 
        root.children.push(inputArray[i]); 
       } 
      } 
     } 

     //now call it recursively 
     len2 = root.children.length; 
     if(len2 > 0){ 
      for(;i2 < len2; i2++){ 
       findChildren(root.children[i2]); 
      } 
     } 
    }; 
    return returnVal; 
}; 

//Then execute it 
finalArray = buildObjectGraph(YOUR_RAW_ARRAY); 
+0

嘿非常感謝!有沒有辦法通過刪除RAW數組中已經被推送的項目來進行優化? –

+0

我真的希望在處理過程中'拼接'輸入數組的各個部分,但是因爲這兩個函數都使用了指向'inputArray'的指針,這會真的搞亂了試圖遍歷每個項目的迭代器。另外,查看拼接不同數組以傳遞給'findChildren'函數的性能,您可能不會更好,因爲這可能會產生一些開銷。 最後,檢查數組的'.length'屬性的確會減慢一點(特別是在大數組上)。首先將它緩存在一個變量中然後循環對它進行循環會更好。 – ericb

0

當您構建「樹木構建器功能」時,您必須確定「頂層事物」是單個項目還是項目數組。既然你說itemS我們去一個數組。不同之處在於你傳入並返回的參數,如果它是一個我們傳遞parentId的數組,那麼我們傳遞該id。

function buildTree(parentId, list) { 
    var nodes = []; 
    for (var i=0, l; l = list[i]; i++) { 
    if (l.parentId === parentId) { 
// if you need "myList" intact afterwards remove the next line at the cost of efficiency 
     list.splice(i, 1); i--; 
     nodes.push({ 
     id: l.id 
     ,parentId: l.parentId 
     ,name: l.name 
     ,children: buildTree(l.id, list) 
     }); 
    } 
    } 
    return nodes; 
} 

var myTree = buildTree(null, myList); 
+0

對於遞歸解決方案(或者說,我已經看到和想到的遞歸解決方案),唯一的主要抱怨是它對於大量節點會非常緩慢。每個非根節點都需要掃描整個數組的子進程,使得算法O(n^2) - 或更糟,除非數組推進是恆定時間的。它在概念上更簡單,但如果需要一天運行,則無關緊要。 :) – cHao

+0

固定 - 刪除元素,因爲他們被發現 - 以及它更好,但仍然不是超快。 – James

+0

不幸的是,AFAIK拼接數組是O(n)操作。對於非常大的數組,這實際上可能會減慢速度,因爲你現在可能在O(n^3)。 – cHao