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 可以参考下面的原回答(mapfindInTree 都可以)。


另外,针对作者的原回答(总算是打开了 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/3num1/2/3,依次累加返回就是需要的结果了。

但是也不知道你要的 Count 的是否只要非0层的。还是说每层都要按照子级的累计。


回答:

参考了一下下面大佬,原回答多了一倍循环。优化性能。
从叶子全算到父级。
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: 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;

})

原回答
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: 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

回到顶部