js树结构所有叶子节点的值相加等于父节点的值?
const tableData = [ {
id: 1,
title: '园区1',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: [
{
id: 11,
parentId: 1,
title: '集团1',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
{
id: 12,
parentId: 1,
title: '集团2',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: [
{
id: 11,
parentId: 12,
title: '公司1',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
{
id: 11,
parentId: 12,
title: '公司2',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
],
},
],
},
{
id: 2,
title: '园区2',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: [
{
id: 21,
parentId: 2,
title: '集团1',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
{
id: 22,
parentId: 2,
title: '集团2',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: [
{
id: 221,
parentId: 22,
title: '公司1',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
{
id: 222,
parentId: 22,
title: '公司2',
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
},
],
},
],
},
]
计算规则
count1要等于所有子集的 count1 相加
count2要等于所有子集的 count2 相加
count3要等于所有子集的 count3 相加
num1要等于所有子集的 num1 相加
num2要等于所有子集的 num2 相加
num3要等于所有子集的 num3 相加
...依此类推
这是demo,麻烦务必请看一眼demo,感觉看页面清晰一点,也能表达明白我的意思
https://element-plus.run/#eyJ...
实现了,但是我是一层一层写的,帮忙看下能否用递归,不然层级是不固定的
https://element-plus.run/#eyJ...
感谢各位提供的思路,已经实现了,谢谢
回答:
2023-02-22 更新
回答的时候魔怔了,两种办法都有点奇怪。实际上只从叶到根依次计算的话,只需要重算每个节点当时子节点的数据之和就行了,不需要全算,也不需要算差值。
function reCalculate(node) { if (!node) { return; }
const resetNode = (node) => {
node.count1 = 0;
node.num1 = 0;
return node;
};
if (node.children?.length) {
node.children.reduce((parent, it) => {
parent.count1 += it.count1;
parent.num1 += it.num1;
return parent;
}, resetNode(node));
}
reCalculate(findNode(node.parentId));
}
其中 findNode
可以参考下面的原回答(map
和 findInTree
都可以)。
另外,针对作者的原回答(总算是打开了 Demo),做了一些优化处理:我认为 findRow()
应该是找一个节点,所以改了下。另外,calc()
实际上是在 calcParent()
,所以我改了个名字。然后 calc 可以优化成循环,不需要使用递归。
修改后的代码:
const findRow = (data: any[], parentId: number) => { // 先在当前节点集合中查找
const node = data.find(it => it.id === parentId);
if (node) { return node; }
// 如果没有就在各自的子节点中去查找(递归)
for (let it of data) {
if (!it.children?.length) { continue; }
const found = findRow(it.children, parentId);
if (found) { return found; }
}
return undefined;
}
// 使用新 findRow 的 calcParent(原 calc)
const calcParent = (row: any, field: string) => {
const { parentId } = row;
const curRow = parentId && findRow(tableData, parentId);
if (!curRow) { return; }
curRow[field] = (curRow.children ?? [])
.reduce((sum, it) => sum + it[field], 0);
calc(curRow);
};
// 去掉了递归
const calc = (row: any, field: string) => {
// calcParent(row, field);
// 下面是非递归算法
let parentId = row.parentId;
while (parentId) {
row = findRow(tableData, parentId);
if (!row) { return; }
row[field] = (row.children ?? []).reduce((sum, it) => sum + it[field], 0);
parentId = row.parentId;
}
}
修改后的 Demo 在这里
下面是原回答
应该需要从上往下递归重算,因为虽然改的只是一叶结点,回溯上去只有一条路径,但是每个父节点上的数据都是其所有子结点数据之和,必须要拿所有子节点的数据来重算。
function reCalculate(node) { if (Array.isArray(node)) { node.forEach(reCalculate); }
if (node.children?.length) {
[node.count1, node.num1] = Array(2).fill(0);
node.children.reduce((r, it) => {
reCalculate(it);
r.count1 += it.count1;
r.num1 += it.num1;
return r;
}, node);
}
}
但是,如果能得到改变量的话,这个过程就可以简化了,只需要按 parentId 一直回溯,使用改变量来修改每个父节点的数据即可。
function reCalculateDelta(root, delta) { // 建立映射表(方便按 parentId 查),可提前准备好
const map = (() => {
const buildMap = (nodes, map = {}) => {
nodes.forEach(it => {
map[it.id] = it;
if (it.children?.length) {
buildMap(it.children, map);
}
});
return map;
};
return buildMap(Array.isArray(root) ? root : [root]);
})();
// 从 delta 对象里拿到 parentId(叶节点已经改过了不需要处理了)
// delta 里包含每一个数据的变化值(不是终值),如果变小就是负数
let { parentId } = delta;
// 循环向上查找处理完所有 parent node
while (parentId) {
const node = map[parentId];
if (node) {
node.count1 += delta.count1;
node.num1 += delta.num1;
}
parentId = node?.parentId;
}
}
试验代码:为了找一个叶节点出来修改,所以写了个简易的 find 函数
function findInTree(nodes, predicate) { return nodes.find(predicate)
?? (() => {
for (let node of nodes) {
if (!node.children?.length) { continue; }
const r = findInTree(node.children, predicate);
if (r) { return r; }
}
})();
}
const target = findInTree(tableData, it => it.id === 221);[target.count1, target.num1] = [3, 5];
// case 2
reCalculateDelta(tableData, { parentId: 22, count1: 3, num1: 5 });
// case 1
// reCalculate(tableData);
console.dir(tableData, { depth: null });
最近写了一篇关于这种计算的博客:树,计算父节点的值
回答:
递归一下就好了,每次判断一下 children
是否有长度。
每次返回一个对象,里面包含 count1/2/3
和 num1/2/3
,依次累加返回就是需要的结果了。
但是也不知道你要的 Count
的是否只要非0层的。还是说每层都要按照子级的累计。
回答:
参考了一下下面大佬,原回答多了一倍循环。优化性能。
从叶子全算到父级。
const tableData = [{id: 1, title: '园区1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 11, parentId: 1, title: '集团1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 12, parentId: 1, title: '集团2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 11, parentId: 12, title: '公司1', count1: 9, count2: 10, count3: 5, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 11, parentId: 12, title: '公司2', count1: 1, count2: 20, count3: 55, num1: 0, num2: 0, num3: 0, num4: 0,},],},],}, {id: 2, title: '园区2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 21, parentId: 2, title: '集团1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 22, parentId: 2, title: '集团2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 221, parentId: 22, title: '公司1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 222, parentId: 22, title: '公司2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,},],},],},]// 深度遍历,callback 每个对象的回调参数
let treeCalc = (obj = {children: []}, callback = (parent, node) => {
}) => {
obj && obj.children && obj.children.forEach(item => {
treeCalc(item, callback);
callback && callback(obj, item);
})
return obj;
}
// 顶级对象
let rootData = {
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: tableData
}
// 根据统计叶子节点数量赋值到父级节点数量
treeCalc(rootData, (parent, node) => {
parent.count1 += node.count1;
parent.count2 += node.count2;
parent.count3 += node.count3;
parent.num1 += node.num1;
parent.num2 += node.num2;
parent.num3 += node.num3;
parent.num4 += node.num4;
})
原回答
const tableData = [{id: 1, title: '园区1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 11, parentId: 1, title: '集团1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 12, parentId: 1, title: '集团2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 11, parentId: 12, title: '公司1', count1: 9, count2: 10, count3: 5, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 11, parentId: 12, title: '公司2', count1: 1, count2: 20, count3: 55, num1: 0, num2: 0, num3: 0, num4: 0,},],},],}, {id: 2, title: '园区2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 21, parentId: 2, title: '集团1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 22, parentId: 2, title: '集团2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0, children: [{id: 221, parentId: 22, title: '公司1', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,}, {id: 222, parentId: 22, title: '公司2', count1: 0, count2: 0, count3: 0, num1: 0, num2: 0, num3: 0, num4: 0,},],},],},]// 深度遍历,callback 每个对象的回调参数
let treeCalc = (obj = {children: []}, callback = (obj) => {
}) => {
obj && obj.children && obj.children.forEach(item => {
treeCalc(item, callback);
})
callback && callback(obj);
return obj;
}
// 顶级对象
let rootData = {
count1: 0,
count2: 0,
count3: 0,
num1: 0,
num2: 0,
num3: 0,
num4: 0,
children: tableData
}
// 根据统计叶子节点数量赋值到父级节点数量
treeCalc(rootData, (obj) => {
obj.children && obj.children.forEach(item => {
obj.count1 += item.count1;
obj.count2 += item.count2;
obj.count3 += item.count3;
obj.num1 += item.num1;
obj.num2 += item.num2;
obj.num3 += item.num3;
obj.num4 += item.num4;
})
})
相关:
深度递归拷贝:不影响原先数据。
https://www.lodashjs.com/docs...
函数防抖:比如输入事件,延迟一秒调用函数。
https://www.lodashjs.com/docs...
回答:
const calc = (row: any, field: string) => { const { parentId } = row
const curRow = findRow(tableData, parentId)
if (Array.isArray(curRow) && curRow.length) {
const sum = curRow[0].children.reduce((prev: { [x: string]: any }, cur: { [x: string]: any }) => {
return prev[field] + cur[field]
})
curRow[0][field] = sum
calc(curRow[0], field)
}
}
以上是 js树结构所有叶子节点的值相加等于父节点的值? 的全部内容, 来源链接: utcz.com/p/933688.html