簡體   English   中英

在javascript中將平面json文件轉換為樹結構

[英]convert a flat json file to tree structure in javascript

我基本上是試圖將平面json文件轉換為樹視圖。 在這里,樹視圖所需的父子關系由使用源和目標的鏈接鍵來維護。

這是示例原始輸入:

{
  "nodes" : [
    {
      name: "bz_db",
      index: 0
    },
    {
      name: "mysql",
      index: 1
    },
    {
      name: "postgres",
      index: 2
    },
    {
      name: "it-infra",
      index: 3
    },
    {
      name: "user-count",
      index: 4
    }
  ],
  links: [
    {
      source: 0, target: 1
    },
    {
      source: 0, target: 3
    },
    {
      source: 1, target: 3
    },
    {
      source: 3, target: 4
    }
  ]
}

如您所見,鏈接字段保持了這種關系,最后我希望我的數據采用以下格式:

{
  name: "bz_db",
  children: [
    {
      name: "mysql",
      children: [
        {
          name: "it-infra",
          children: [
            {
              name: "user_count",
              children: []
            }
          ]
        }
      ]
    },
    {
      name: "it-infra",
      children: [{
          name: "user_count",
          children: []
        }
      ]
    }
  ]
}

我試圖解決此問題,但它只適用於1級(以顯示所選根元素的直接子級)。

var findObjectByKeyValue = function(arrayOfObject, key, value){
                    return _.find(arrayOfObject, function(o){ return o[key] == value})
                }

var rootObject = findObjectByKeyValue(sample_raw_input.nodes, 'name', 'bz_db');
var treeObject = {
                        name: rootObject.name,
                        index: rootObject.index,
                        root: true,
                        children: []
                    };
angular.forEach(dependencyData.links, function(eachLink){
                        if(treeObject.index == eachLink.source){
                            var rawChildObject = findObjectByKeyValue(dependencyData.nodes, 'index', eachLink.target);
                            var childObject = {};
                            childObject.index = rawChildObject.index;
                            childObject.name = rawChildObject.name;
                            childObject.children = [];
                            treeObject.children.push(childObject);
                        }
                    });

但是上面的代碼只返回第一層依賴關系,但是我想要層次關系。 我知道我可以在這里使用遞歸。 但是我對此不太滿意。

Josh的答案使用了一系列map > filter > map > find調用,每個調用都通過一組數據進行迭代。 隨着集合中節點數量的增加,這種循環的循環會導致驚人的計算復雜性。

您可以通過在每個nodeslinks上使用一次reduce來大大簡化樹的創建。 與Array需要線性時間(較慢)的find相比, Map還可以以對數時間執行查找。 當您考慮為輸入的每個元素調用此操作時,很明顯會看到時間上的顯着差異。

const makeTree = (nodes = [], links = []) =>
  links.reduce
    ( (t, l) =>
        t.set ( l.source
              , MutableNode.push ( t.get (l.source)
                                 , t.get (l.target)
                                 )
              )
    , nodes.reduce
        ( (t, n) => t.set (n.index, MutableNode (n.name))
        , new Map
        )
    )
    .get (0)

最后,我們提供了我們依賴的MutableNode接口

const MutableNode = (name, children = []) =>
  ({ name, children })

MutableNode.push = (node, child) =>
  (node.children.push (child), node)

以下是完整的程序演示。 JSON.stringify僅用於顯示結果

 const MutableNode = (name, children = []) => ({ name, children }) MutableNode.push = (node, child) => (node.children.push (child), node) const makeTree = (nodes = [], links = []) => links.reduce ( (t, l) => t.set ( l.source , MutableNode.push ( t.get (l.source) , t.get (l.target) ) ) , nodes.reduce ( (t, n) => t.set (n.index, MutableNode (n.name)) , new Map ) ) .get (0) const data = { nodes: [ { name: "bz_db", index: 0 } , { name: "mysql", index: 1 } , { name: "postgres", index: 2 } , { name: "it-infra", index: 3 } , { name: "user-count", index: 4 } ] , links: [ { source: 0, target: 1 } , { source: 0, target: 3 } , { source: 1, target: 3 } , { source: 3, target: 4 } ] } const tree = makeTree (data.nodes, data.links) console.log (JSON.stringify (tree, null, 2)) 

您可以依靠跟蹤對象引用,而無需任何遞歸。 使用Object.assign ,將節點列表映射到其子節點:

// Assuming that input is in `input`
const nodes = input.nodes.reduce((a, node) => {
  a[node.index] = { ...node, index: undefined };
  return a;
}, []);

// organize the links by their source
const links = input.links.reduce((a, link) => {
  return a.set((a.get(link.source) || []).concat(nodes[link.target]);
}, new Map());

// Apply side effect of updating node children
nodes.forEach(node => Object.assign(node, {
  children: links.get(node.index),
}));

因此,我獲取了節點列表,並為每個節點分配了一個新數組(以改變節點本身,請記住,這是一個副作用)。 這些children節點是鏈接此節點的所有鏈接,我們Array#map將它們轉換為target ID到所需的實際節點。

只是分享樣本,與您的樣本略有不同。 但這給您遞歸函數的提示。

jsFiddle平面數組json轉換為遞歸樹json

            function getNestedChildren(arr, parent) {
            var out = []
            for(var i in arr) {
                if(arr[i].parent == parent) {
                    var children = getNestedChildren(arr, arr[i].id)

                    if(children.length) {
                        arr[i].children = children
                    }
                    out.push(arr[i])
                }
            }
            return out
        }


        var flat = [
            {id: 1, title: 'hello', parent: 0},
            {id: 2, title: 'hello', parent: 0},
            {id: 3, title: 'hello', parent: 1},
            {id: 4, title: 'hello', parent: 3},
            {id: 5, title: 'hello', parent: 4},
            {id: 6, title: 'hello', parent: 4},
            {id: 7, title: 'hello', parent: 3},
            {id: 8, title: 'hello', parent: 2}
        ]


        var nested = getNestedChildren(flat, 0)

        console.log(nested)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM