Javascript 在深度嵌套对象上递归迭代以查找父对象
我有一个包含子项的数据树结构:Javascript 在深度嵌套对象上递归迭代以查找父对象,javascript,ecmascript-6,Javascript,Ecmascript 6,我有一个包含子项的数据树结构: { id: 1, name: "Dog", parent_id: null, children: [ { id: 2, name: "Food", parent_id: 1, children: [] }, { id: 3, name:
{ 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的选定项的已知文本标题以及用户想要插入的数据。我很难找到一个递归算法,它允许我将新数据添加到树的正确级别
下面是我思考问题并尝试对其进行伪代码的列表:
投入:
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;
}
因此,如果用户在单击“Dog”的添加按钮时键入“Cat”,则会创建一个新对象
{
id: 7,
name: "Cat",
parent_id: 1,
children: []
}
将被插入到数据树中第一级“Dog”对象的子对象中。如果需要递归解决方案,应修改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。如果可以使用Lodash+,则:
不太喜欢重新发明轮子,尤其是在算法方面。以下是您可以使用的解决问题的方法。我们使用它进行数据处理,因为一旦你把脑袋绕在它身上,它就非常强大了
//const objectScan=require('object-scan');
const insert=(树、父名、子名)=>objectScan(['**.name']{
中止:对,
rtn:‘bool’,
filterFn:({value,parent})=>{
如果(值===父名称){
父、子、推({
id:Math.max(…objectScan(['**.id']),{rtn:'value'}(树))+1,
姓名:childName,
parent_id:parent.id,
儿童:[]
});
返回true;
}
返回false;
}
})(树木);
const data={id:1,name:'Dog',parent_id:null,children:[{id:2,name:'Food',parent_id:1,children:[]},{id:3,name:'hood',parent_id:3,children:[]},{id:5,name:'hood',parent_id:3,children children:[]},{id:6,name:'Hydrogen',parent:'Hydrogen:'id:3,children id:3,children[]}}};
console.log(插入(数据“Dog”、“Cat”);//正确的iff插入成功
//=>正确
控制台日志(数据);
//=>{id:1,名字:'Dog',父母id:null,孩子:[{id:2,名字:'Food',父母id:1,孩子:[]},{id:3,名字:'Water',父母id:1,孩子:[{id:4,名字:'Bowl',父母id:3,孩子:[]},{id:5,名字:'Oxygen oxy',父母id:3,孩子:[]},{id:6,名字:'Hydrogen',父母id:3,孩子:[]},{id:7,名称:'Cat',父\u id:1,子:[]}
。作为控制台包装{最大高度:100%!重要;顶部:0}
在生成时,您可以通过闭包将侦听器直接附加到保持所需节点的按钮,然后就可以像node.children.push({/*…*/})
@jonaswillms我无法影响或更改侦听器一样简单。
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;