【问题标题】:Recursive tree search in a nested object structure in JavaScriptJavaScript中嵌套对象结构中的递归树搜索
【发布时间】:2019-02-03 14:11:03
【问题描述】:

我试图弄清楚如何递归地在这个 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);
};

【问题讨论】:

    标签: javascript json recursion tree


    【解决方案1】:

    您的代码只是缺少一个循环来检查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。

    【讨论】:

      【解决方案2】:

      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
        )

      相关recursive search using higher-order functions

      【讨论】:

        【解决方案3】:

        这是使用object-scan的解决方案

        // 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}
        &lt;script src="https://bundle.run/object-scan@13.7.1"&gt;&lt;/script&gt;

        免责声明:我是object-scan的作者

        【讨论】:

          猜你喜欢
          • 2023-04-04
          • 2014-04-08
          • 1970-01-01
          • 2016-12-08
          • 1970-01-01
          • 1970-01-01
          • 2011-10-02
          • 1970-01-01
          相关资源
          最近更新 更多