簡體   English   中英

JavaScript 嵌套對象結構中的遞歸樹搜索

[英]Recursive tree search in a nested object structure in JavaScript

我試圖弄清楚如何在這個 JSON 對象中遞歸搜索節點。 我嘗試過一些東西但無法得到它:

var tree = {
    "id": 1,
    "label": "A",
    "child": [
        {
            "id": 2,
            "label": "B",
            "child": [
                {
                    "id": 5,
                    "label": "E",
                    "child": []
                },
                {
                    "id": 6,
                    "label": "F",
                    "child": []
                },
                {
                    "id": 7,
                    "label": "G",
                    "child": []
                }
            ]
        },
        {
            "id": 3,
            "label": "C",
            "child": []
        },
        {
            "id": 4,
            "label": "D",
            "child": [
                {
                    "id": 8,
                    "label": "H",
                    "child": []
                },
                {
                    "id": 9,
                    "label": "I",
                    "child": []
                }
            ]
        }
    ]
};

這是我的非工作解決方案,這可能是因為第一個節點只是一個值,而孩子在數組中:

function scan(id, tree) {
    if(tree.id == id) {
        return tree.label;
    }

    if(tree.child == 0) {
        return
    }

    return scan(tree.child);
};

您的代碼只是缺少一個循環來檢查child數組中節點的每個子節點。 如果標簽不存在於樹中,此遞歸函數將返回節點的label屬性或undefined

 const search = (tree, target) => { if (tree.id === target) { return tree.label; } for (const child of tree.child) { const found = search(child, target); if (found) { return found; } } }; const tree = {"id":1,"label":"A","child":[{"id":2,"label":"B","child":[{"id":5,"label":"E","child":[]},{"id":6,"label":"F","child":[]},{"id":7,"label":"G","child":[]}]},{"id":3,"label":"C","child":[]},{"id":4,"label":"D","child":[{"id":8,"label":"H","child":[]},{"id":9,"label":"I","child":[]}]}]}; console.log(search(tree, 1)); console.log(search(tree, 6)); console.log(search(tree, 99));

您還可以使用不會導致堆棧溢出的顯式堆棧迭代地執行此操作(但請注意,速記stack.push(...curr.child);由於擴展語法可能會溢出某些 JS 引擎的參數大小,因此對大量子數組使用顯式循環或concat ):

 const search = (tree, target) => { for (const stack = [tree]; stack.length;) { const curr = stack.pop(); if (curr.id === target) { return curr.label; } stack.push(...curr.child); } }; const tree = {"id":1,"label":"A","child":[{"id":2,"label":"B","child":[{"id":5,"label":"E","child":[]},{"id":6,"label":"F","child":[]},{"id":7,"label":"G","child":[]}]},{"id":3,"label":"C","child":[]},{"id":4,"label":"D","child":[{"id":8,"label":"H","child":[]},{"id":9,"label":"I","child":[]}]}]}; for (let i = 0; ++i < 12; console.log(search(tree, i)));

更通用的設計將返回節點本身,並讓調用者訪問.label屬性(如果他們願意),或者以其他方式使用對象。

請注意,JSON 純粹是序列化(字符串化、原始)數據的字符串格式。 一旦您將 JSON 反序列化為 JavaScript 對象結構,就像這里一樣,它就不再是 JSON。

可以使用第三個參數遞歸寫入scan該參數對要掃描的節點隊列進行建模

const scan = (id, tree = {}, queue = [ tree ]) =>
  // if id matches node id, return node label
  id === tree.id
    ? tree.label

  // base case: queue is empty
  // id was not found, return false
  : queue.length === 0
    ? false

  // inductive case: at least one node
  // recur on next tree node, append node children to queue
  : scan (id, queue[0], queue.slice(1).concat(queue[0].child))

因為 JavaScript 支持默認參數,所以scan的調用點不變

console.log
  ( scan (1, tree)  // "A"
  , scan (3, tree)  // "C"
  , scan (9, tree)  // "I"
  , scan (99, tree) // false
  )

在下面的瀏覽器中驗證它是否有效

 const scan = (id, tree = {}, queue = [ tree ]) => id === tree.id ? tree.label : queue.length === 0 ? false : scan (id, queue[0], queue.slice(1).concat(queue[0].child)) const tree = { id: 1 , label: "A" , child: [ { id: 2 , label: "B" , child: [ { id: 5 , label: "E" , child: [] } , { id: 6 , label: "F" , child: [] } , { id: 7 , label: "G" , child: [] } ] } , { id: 3 , label: "C" , child: [] } , { id: 4 , label: "D" , child: [ { id: 8 , label: "H" , child: [] } , { id: 9 , label: "I" , child: [] } ] } ] } console.log ( scan (1, tree) // "A" , scan (3, tree) // "C" , scan (9, tree) // "I" , scan (99, tree) // false )

使用高階函數進行相關遞歸搜索

這是使用對象掃描的解決方案

 // const objectScan = require('object-scan'); const tree = {"id":1,"label":"A","child":[{"id":2,"label":"B","child":[{"id":5,"label":"E","child":[]},{"id":6,"label":"F","child":[]},{"id":7,"label":"G","child":[]}]},{"id":3,"label":"C","child":[]},{"id":4,"label":"D","child":[{"id":8,"label":"H","child":[]},{"id":9,"label":"I","child":[]}]}]}; const search = (obj, id) => objectScan(['**.id'], { abort: true, filterFn: ({ value, parent, context }) => { if (value === id) { context.push(parent.label); return true; } return false; } })(obj, [])[0]; console.log(search(tree, 1)); // => A console.log(search(tree, 6)); // => F console.log(search(tree, 99)); // => undefined
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.7.1"></script>

免責聲明:我是對象掃描的作者

暫無
暫無

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

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