代码之家  ›  专栏  ›  技术社区  ›  DrCord

对嵌套较深的对象进行递归迭代以找到父对象

  •  1
  • DrCord  · 技术社区  · 6 年前

    {  id: 1,
       name: "Dog",
       parent_id: null,
       children: [
             {
                 id: 2,
                 name: "Food",
                 parent_id: 1,
                 children: []
             },
             {
                 id: 3,
                 name: "Water",
                 parent_id: 1,
                 children: [
                     {
                        id: 4,
                        name: "Bowl",
                        parent_id: 3,
                        children: []
                     },
                     {
                        id: 5,
                        name: "Oxygen",
                        parent_id: 3,
                        children: []
                     },
                     {
                        id: 6,
                        name: "Hydrogen",
                        parent_id: 3,
                        children: []
                     }
                 ]
             }
       ]
    }
    

    任何子数据对象都可以有更多的子对象,如上面的数据所示。这表示一个DOM结构,用户可以从中选择要添加子项的项。

    我有一个从DOM中选择的项的已知文本标题以及用户想要插入的数据。我很难找到一个递归算法,使我能够添加新的数据到树的正确水平。

    下面是我思考问题并尝试对其进行伪代码的列表:

    输入:

    1. DOM中单击项的parentTitle

    输出:

    1. 确定最高使用id以知道下一个唯一id是什么
    2. 检查当前数据级别是否与父级标题匹配
    3. 如果匹配,则在新数据中设置id和parent\u id并推入父对象的子对象
    4. 如果不匹配,则检查当前级别数据是否有子级
    5. 如果当前级别有子级,则需要对每个子级重复步骤2+,直到找到匹配项为止

    这是我的密码:

    function askUserForNewItem(e) {
       const tree = getTree(); // returns above tree data structure
       const name = prompt( 'Enter new item’s name:' ); // user input to match and insert as new item in tree
       const clickedTitle = getClickedTitle(e); // returns string title of clicked on item from DOM - for example "Dog" or "Bowl"
       const parent = determineParent(tree, clickedTitle);
       const parent_id = parent[0].id;
       // TODO - needs to set real unique id (highest unused id)
       const newId = 101; // hard coded for now, needs to be dynamic
       // TODO - needs to insert into correct level of children array in tree
       return tree.children.push({ id: newId, name, emoji, children: [], parent_id: parent_id });
    }
    
    function determineParent(tree, clickedTitle) {
    
        if(tree.children.length === 0) {
            return false;
        }
        let treeLevel = tree;
        let parent = [];
        while(treeLevel.children.length !== 0) {
            parent = treeLevel.children.filter(child => child.name === clickedTitle);
            if(parent.length !== 0) {
                break;
            }
            else {
                // what to do here to make this recursive?
            }
        }
    
        return parent;
    }
    

    {
        id: 7,
        name: "Cat",
        parent_id: 1,
        children: []
    }
    

    0 回复  |  直到 6 年前
        1
  •  1
  •   MikNiller    6 年前

    如果您想要一个递归的解决方案,您应该修改determineParent方法,以便它沿着树向下搜索。 我不确定这正是你要找的,但我希望你能大致了解一下

    function determineParent(curNode, clickedTitle) {
        if(curNode.name===clickedTitle)
            return curNode; // found the parent node with the correct clickedTitle
    
        // not found yet, do a recusive search down the tree.
    
        for(const node of curNode.children) {
            return determineParent(node,clickedTitle);
        }
    
        return null; // not found.
    }
    

    其思想是从最顶层的节点(curNode)开始,首先确定它是否是正确的父节点,如果不是,则取第一个子节点查看它是否匹配,如果不匹配,则向下搜索它的子节点,依此类推。

    在处理递归时,可能需要处理可能遇到循环引用的情况,考虑这样一种情况:节点有一个子节点指向节点的父节点或父节点,递归方法将永远运行(在现实生活中,它将耗尽堆栈空间并引发异常)。

    function determineParent(curNode, clickedTitle, safeGuard) {
        if(curNode.name===clickedTitle)
            return curNode; // found the parent node with the correct clickedTitle
    
        if(safeGuard===0)
            return null; // bail out 
    
        // not found yet, do a recusive search down the tree.
        for(const node of curNode.children) {
            return determineParent(node,clickedTitle,--safeGuard);
        }
    
        return null; // not found.
    }
    

    然后像这样称呼它

      this.determineParent(tree,"title",100);
    

    将搜索次数限制为100。

        2
  •  0
  •   Yuri Gor    6 年前

    Deepdash ,然后:

    let child = {
        name: "Cat",
        children: []
    };
    let maxUsedId=-1;
    _.eachDeep([data],(val)=>{
      if(val.id>maxUsedId){
        maxUsedId = val.id;
      }
    
      if(val.name==parentName){
        val.children.push(child);
        child.parent_id = val.id;
      }
    },{tree:true});
    child.id=maxUsedId+1;
    

    Codepen for this

        3
  •  0
  •   vincent    4 年前

    不太喜欢重新发明轮子,尤其是在算法方面。以下是您可以使用 object-scan

    // const objectScan = require('object-scan');
    
    const insert = (tree, parentName, childName) => objectScan(['**.name'], {
      abort: true,
      rtn: 'bool',
      filterFn: ({ value, parent }) => {
        if (value === parentName) {
          parent.children.push({
            id: Math.max(...objectScan(['**.id'], { rtn: 'value' })(tree)) + 1,
            name: childName,
            parent_id: parent.id,
            children: []
          });
          return true;
        }
        return false;
      }
    })(tree);
    
    const data = { id: 1, name: 'Dog', parent_id: null, children: [{ id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [{ id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 5, name: 'Oxygen', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] }] }] };
    
    console.log(insert(data, 'Dog', 'Cat')); // true iff insert was successful
    // => true
    
    console.log(data);
    // => { id: 1, name: 'Dog', parent_id: null, children: [ { id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [ { id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 5, name: 'Oxygen', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] } ] }, { id: 7, name: 'Cat', parent_id: 1, children: [] } ] }
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://bundle.run/object-scan@13.7.1"></script>

    免责声明 :我是 object-scan