Javascript从一个非常奇怪的数组构建嵌套对象(树)调试



我真的需要一些帮助来调试一个问题。我试图从一个非常不寻常的数组中构建一个嵌套的对象(树(,我从后端获得(不能访问BE,所以我必须使用我得到的(。我已经完成了95%,但我遇到了错误

我知道StackOverFlow中有很多嵌套对象的平面数组,但我要使用的这个数组却大不相同。它没有展平数组的id或典型属性。好的是我已经完成了大部分。

以下是我遇到的问题的一个基本示例:

数据:


let stackTest = {
predicates: [
{
primaryTerm: "test1",
level: 1,
condition: 0,
},
{
primaryTerm: "and",
level: 1,
condition: 2,
},
{
primaryTerm: "test2",
level: 1,
condition: 0,
},
{
primaryTerm: "and",
level: 1,
condition: 2,
},
{
primaryTerm: "test3",
level: 2,
condition: 0,
},
{
primaryTerm: "or",
level: 2,
condition: 1,
},
{
primaryTerm: "test4",
level: 2,
condition: 0,
},
{
primaryTerm: "or",
level: 2,
condition: 1,
},
{
primaryTerm: "test5",
level: 3,
condition: 0,
},
{
primaryTerm: "and",
level: 3,
condition: 2,
},
{
primaryTerm: "test5",
level: 3,
condition: 0,
},

]

所需功能输出:


[
{
id: 1,
children: [
{
id: 2,
children: [],
parentId: 1,
level: 1,
primaryTerm: 'test1',
condition: 0
},
{
id: 3,
children: [],
parentId: 1,
primaryTerm: 'test2',
level: 1,
condition: 0
},
{
id: 4,
children: [
{
primaryTerm: 'test3',
level: 2,
condition: 0,
id: 5,
children: [],
parentId: 4
},
{
id: 6,
children: [],
parentId: 4,
primaryTerm: 'test4',
level: 2,
condition: 0
},

{
id: 8,
children: [
{
primaryTerm: 'test5',
level: 3,
condition: 0,
id: 9,
children: [],
parentId: 8
},
{
id: 10,
children: [],
parentId: 8,
primaryTerm: 'test5',
level: 3,
condition: 0
}
],
parentId: 4,
level: 2,
primaryTerm: 'and',
condition: 2
}

],
parentId: 0,
level: 1,
primaryTerm: 'or',
condition: 1
}
],
parentId: 0,
level: 0,
primaryTerm: 'and',
condition: 2
}
]

期望的树表示:


                                           AND
                                    /       |       
                               Test1      Test2        OR
                                                     /    |           
                                                   Test3  Test4   AND
                   
|   
Test5   Test6

实际功能输出:


[
{
id: 1,
children: [
{
id: 2,
children: [],
parentId: 1,
level: 1,
primaryTerm: 'test1',
condition: 0
},
{
id: 3,
children: [],
parentId: 1,
primaryTerm: 'test2',
level: 1,
condition: 0
},
{
id: 4,
children: [
{
primaryTerm: 'test3',        
level: 2,
condition: 0,
id: 5,
children: [],
parentId: 4
},
{
id: 6,
children: [],
parentId: 4,
primaryTerm: 'test4',
level: 2,
condition: 0
},
{
id: 7,
children: [
{
id: 8,
children: [
{
primaryTerm: 'test5',
level: 3,
condition: 0,
id: 9,
children: [],
parentId: 8
},
{
id: 10,
children: [],
parentId: 8,
primaryTerm: 'test5',
level: 3,
condition: 0
}
],
parentId: 7,
level: 2,
primaryTerm: 'and',
condition: 2
}
],
parentId: 1,
level: 1
}
],
parentId: 0,
level: 1,
primaryTerm: 'or',
condition: 1
}
],
parentId: 0,
level: 0,
primaryTerm: 'and',
condition: 2
}
]

实际树表示:


                                           AND
                                  /          |     
                         Test1          Test2          OR
                                                  /     |           
                                              Test3   Test4    {}  --here is the issue

                           
AND
                       
|     
                    
Test5    Test6

问题:正在创建一个额外的空对象,该对象根本不应该存在。不确定为什么要创建这个额外的节点。我怀疑这可能与buildEmptyFlatArr有关,它可能没有正确地附加parentId,因为它也被搞砸了。只有当嵌套分支位于树的右侧时才会出现此问题,如果嵌套分支位于该树的左侧,则不会出现此问题。

到目前为止我所做的:

规则:具有"one_answers"/"或"的对象被视为逻辑运算符只有逻辑(AND/OR(对象才能有子对象步骤

  • 1:将嵌套的obj(树(初始化到第一个谓词:

buildEmptyFlatArr和listToTree函数处理这个

  • 2:当我们到达(And/OR(对象时,将逻辑容器的属性赋予新节点,其中该空节点的级别匹配(logicalObject级别-1(

updatedTreeNode函数处理这个

  • 3:如果数组中的以下项与上一个逻辑对象具有相同级别,则将以下对象附加到逻辑对象来自步骤#2的操作员。如果级别不同,请附加一个空节点,直到达到当前循环对象的级别

updatedTreeNode函数处理这个

代码:



let id = 1;
let lastLogical = {};
let lastItemAdded = {};
let initTree;


const buildEmptyFlatArr = (num, branch = false, branchLastValue) =>{
let initArr = [];

for (let i = 0; i  {
let map = {},
node,
roots = [],
i;

for (i = 0; i  {
tree.forEach((item, index) =>{
if (logical) {

if (item.level === target.level - 1 
) {
let objToMod = item;

if (item.condition === undefined) {
for (const [key, value] of Object.entries(target)) {
if (key === "level") {
objToMod["level"] = target.level - 1;
} else {
objToMod[key] = value;
}
}
}

lastLogical = item;
lastItemAdded = item;

} else if (item.children.length >0 ) {
lastLogical = item;
updateTreeNode(item.children, target, first, logical);
}
} else if (first) {
if (item.level === target.level) {
let objToMod = item;
for (const [key, value] of Object.entries(target)) {
objToMod[key] = value;
}
lastItemAdded = item;
return;
} else if (item.children !== null && item.children.length >0) {
updateTreeNode(item.children, target, first, logical);
}
} else if (!first && !logical) {
if (Math.abs(lastLogical.level - target.level) >1) {
let buildArrayLength =  target.level - 1;
let arrToAppend = buildEmptyFlatArr(buildArrayLength, true, target);
let newBranchToAppend = listToTree(arrToAppend);

lastLogical.children = [...lastLogical.children, { ...newBranchToAppend[0] }];
lastItemAdded = item;
lastLogical = item;

} else if (item.level === target.level - 1 && lastLogical === item) {
let objToMod = item;
objToMod.children = [
...objToMod.children,
{ id: id, children: [], parentId: item.id, ...target },
];
id += 1;
lastItemAdded = item;
lastLogical = item
} else {
updateTreeNode(item.children, target);
}
}
});
};

const buildTree = (templateData) =>{
templateData.forEach((item, index) =>{
if (index === 0) {
let list = buildEmptyFlatArr(item.level);
initTree = listToTree(list);
updateTreeNode(initTree, item, true, false);
} else if (item.condition === 1 || item.condition === 2) {
updateTreeNode(initTree, item, false, true);
} else {
updateTreeNode(initTree, item);
}
});
};



buildTree(templateObj8.predicates);
console.dir(initTree, { depth: null });

EDIT:我没有很好地提出这个问题(我认为是第一个问题(,作为树的外观的附加示例。我给出的第一个例子是一棵向右加权的树。但是树应该能够处理像这样更复杂的树

第二组数据


let stackTest = {
predicates: [
{
primaryTerm: "test1",
level: 3,
condition: 0,
},
{
primaryTerm: "and",
level: 3,
condition: 2,
},
{                          
primaryTerm: "test2",
level: 3,
condition: 0,
},
{
primaryTerm: "and",
level: 1,
condition: 2,
},
{
primaryTerm: "test3",
level: 2,
condition: 0,
},
{
primaryTerm: "or",
level: 2,
condition: 1,
},
{
primaryTerm: "test4",
level: 2,
condition: 0,
},
{
primaryTerm: "or",
level: 2,
condition: 1,
},
{
primaryTerm: "test5",
level: 3,
condition: 0,
},
{
primaryTerm: "and",
level: 3,
condition: 2,
},
{
primaryTerm: "test5",
level: 3,
condition: 0,
},
{
primaryTerm: "and",
level: 1,
condition: 2,
},
{
primaryTerm: "test6",
level: 2,
condition: 0,
},
{
primaryTerm: "or",
level: 2,
condition: 1,
},
{
primaryTerm: "test7",
level: 2,
condition: 0,
},

]

我的代码的当前输出


[
{
id: 1,
children: [
{
id: 2,
children: [
{
id: 3,
children: [
{
id: 4,
children: [],
parentId: 3,
level: 3,
primaryTerm: 'test1',
condition: 0
},
{
id: 5,
children: [],
parentId: 3,
primaryTerm: 'test2',
level: 3,
condition: 0
}
],
parentId: 2,
level: 2,
primaryTerm: 'and',
condition: 2
}
],
parentId: 1,
level: 1,
primaryTerm: 'or',
condition: 1
},
{
id: 6,
children: [
{
primaryTerm: 'test3',
level: 2,
condition: 0,
id: 7,
children: [],
parentId: 6
},
{
id: 8,
children: [],
parentId: 6,
primaryTerm: 'test4',
level: 2,
condition: 0
},
{
id: 9,
children: [
{
id: 10,
children: [
{
primaryTerm: 'test5',
level: 3,
condition: 0,
id: 11,
children: [],
parentId: 10
},
{
id: 12,
children: [],
parentId: 10,
primaryTerm: 'test5',
level: 3,
condition: 0
}
],
parentId: 9,
level: 2,
primaryTerm: 'and',
condition: 2
}
],
parentId: 1,
level: 1
}
],
parentId: 0,
level: 1,
primaryTerm: 'or',
condition: 1
},
{
id: 13,
children: [
{
primaryTerm: 'test6',
level: 2,
condition: 0,
id: 14,
children: [],
parentId: 13
},
{
id: 15,
children: [],
parentId: 13,
primaryTerm: 'test7',
level: 2,
condition: 0
}
],
parentId: 0,
level: 1,
primaryTerm: 'or',
condition: 1
}
],
parentId: 0,
level: 0,
primaryTerm: 'and',
condition: 2
}
]

这是我的函数的当前输出,test6和test7已正确插入。空对象所在的位置是插入额外节点的位置。如果这个多余的空物体不在那里,它将是完美的


                                       AND
                                  /     |     
                             OR         OR          OR
                           /         /  |           |         
                     AND       test3 test4  {}      test6  test7                                             
/                        |  
test1    test2               and                          
/                                                   
test5  test5                    
                       

                    

正如您所看到的,id为#9的对象是一个不应该存在的额外对象,这就是正在处理的问题。谢谢

这是一种为每个级别的运算符提供数组的方法。

该树始终包含以运算符为根、以操作数为叶的子树。任何操作数也可以包含子树。

如果某个级别比以前的级别更深,则会创建一个新的子树,其中操作数位于顶部,操作数的子级位于顶部。

const
getTree = predicates => {
const
result = [],
levels = [{ children: result }];
let id = 1,
lastLevel = -Infinity;
predicates.forEach(o => {
const level = o.level;
if (level > lastLevel) {
let l = level - 1;
while (!levels[l]) l--;
while (l < level) {
// generate new operator
// assign children to levels
levels[l].children.push(levels[l + 1] = { id, parentId: levels[l]?.id || 0, children: [] });
id++;
l++;
}
}
if (o.condition) { // update operators
if (!levels[level].condition) Object.assign(levels[level], o);
} else { // assign levels
levels[level].children.push({ id, parentId: levels[level].id, ...o, children: [] });
id++;
}
lastLevel = level;
});
return result;    
},    
predicates1 = [{ primaryTerm: "test1", level: 1, condition: 0 }, { primaryTerm: "and", level: 1, condition: 2, }, { primaryTerm: "test2", level: 1, condition: 0 }, { primaryTerm: "and", level: 1, condition: 2 }, { primaryTerm: "test3", level: 2, condition: 0 }, { primaryTerm: "or", level: 2, condition: 1 }, { primaryTerm: "test4", level: 2, condition: 0 }, { primaryTerm: "or", level: 2, condition: 1 }, { primaryTerm: "test5", level: 3, condition: 0 }, { primaryTerm: "and", level: 3, condition: 2 }, { primaryTerm: "test5", level: 3, condition: 0 }],
predicates2 = [{ primaryTerm: "test1", level: 3, condition: 0 }, { primaryTerm: "and", level: 3, condition: 2 }, { primaryTerm: "test2", level: 3, condition: 0 }, { primaryTerm: "and", level: 1, condition: 2 }, { primaryTerm: "test3", level: 2, condition: 0 }, { primaryTerm: "or", level: 2, condition: 1 }, { primaryTerm: "test4", level: 2,  condition: 0 }, { primaryTerm: "or", level: 2, condition: 1 }, { primaryTerm: "test5",level: 3,  condition: 0 }, { primaryTerm: "and",level: 3,  condition: 2 }, { primaryTerm: "test5", level: 3, condition: 0 }, { primaryTerm: "and", level: 1, condition: 2 }, { primaryTerm: "test6", level: 2, condition: 0 }, { primaryTerm: "or", level: 2, condition: 1 }, { primaryTerm: "test7", level: 2, condition: 0 }];

console.log(getTree(predicates1));
console.log(getTree(predicates2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

这应该会让你开始:

function parse(tokens, level) {
let expr = []
while (tokens.length) {
let tok = tokens[0]
if (tok.level < level) {
break;
}
if (tok.level === level) {
expr.push(tok.primaryTerm);
tokens.shift();
continue;
}
expr.push(parse(tokens, tok.level))
}
return {op: expr[1], args: expr.filter((_, n) => n % 2 === 0)}
}

对于您的输入,parse(stackTest.predicates, 1)返回此AST:

{
"op": "and",
"args": [
"test1",
"test2",
{
"op": "or",
"args": [
"test3",
"test4",
{
"op": "and",
"args": [
"test5",
"test5"
]
}
]
}
]
}

最新更新