在深层嵌套对象上进行递归迭代以找到父对象

时间:2019-03-09 17:46:46

标签: javascript ecmascript-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. 树(上面的数据)
  2. 来自DOM中单击项的父标题

输出:

  1. 插入项目的树

步骤:

  1. 确定最高使用的ID,以了解下一个唯一ID是什么
  2. 检查当前数据级别是否与父标题匹配
  3. 如果匹配,则在新数据中设置id和parent_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: []
}

将被插入数据树中第一级“ Dog”对象的子级。

3 个答案:

答案 0 :(得分:1)

如果要使用递归解决方案,则应修改defineParent方法,使其在树中向下搜索。 不确定这正是您要搜索的内容,但我希望您能大致了解

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。

答案 1 :(得分:0)

如果可以使用Lodash + 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

答案 2 :(得分:0)

不大喜欢重新发明轮子,特别是在算法方面。这是使用object-scan解决问题的方法。我们将它用于数据处理,因为一旦您将其包裹起来,它就会非常强大

java -jar [mvn-created-jar-file-name] --spring.profiles.active=dev