数据库存储结构为平铺的结构:
[
{id:1 , parentId: 0, name: '', level: 0},
{id:2 , parentId: 0, name: '', level: 0},
{id:3 , parentId: 2, name: '', level: 1},
{id:4 , parentId: 2, name: '', level: 1},
{id:5 , parentId: 4, name: '', level: 2},
]
需要转换成树状结构:
{
children:[
{
id: 1,
name: ''
},
{
id: 2,
name: '',
children: [
{
id: 3
},
{
id: 4,
children: [
{
id: 5
}
]
}
]
},
]
}
算法如下:
const setPath = (parent, arr) => {
const children = arr.filter(item => item.parentId === parent.id);
children.forEach((child, i) => {
child.path = [ ...parent.path, i ];
setPath(child, arr);
});
const myfn = path => {
return path.map(p => `children[${p}]`).join('.');
};
const obj = {};
return fn => {
if (!fn) fn = myfn;
arr = arr.filter(item => item.path !== undefined);
arr.sort((a, b) => {
return a.path.length - b.path.length;
});
arr.forEach(item => {
_.set(obj, fn(item.path), item);
});
return obj;
};
};
const res = yield dao.query({ solutionId }); // 从数据库拿到数据平铺的结构
generateMethod=undefined 一般情况下不用传生成方法
//如果有需要,比如只要二级结构,不需要到3级别,那么改变generateMethod即可
/*
generateMethod = path => {
if (path.length === 2) return 'nouse';
return path.map(p => `children[${p}]`).join('.');
}
*/
const tree = setPath({ id: 0, path: [] }, res)(generateMethod);
树既然生成了,如何通过给定的叶子节点拿到剪修过的树。树的分支只包含到给定的叶子,其余的分支修剪掉。
const shake = (tree, fn) => {
const arr = [];
const path = [];
const a = {};
function find(node, path) {
if (node.children) {
node.children.forEach((child, i) => find(child, [ ...path, i ]));
} else {
if (fn(node)) {
arr.push(path);
}
}
}
function deleteNullNode(tree) {
if (tree.children) {
tree.children = tree.children.filter(c => c);
tree.children.forEach(c => deleteNullNode(c));
}
}
find(tree, path);
arr.forEach(p => {
for (let i = 1; i <= p.length; i++) {
const _p = p.slice(0, i).map(c => `children[${c}]`).join('.');
if (_.has(a, _p)) {
continue;
}
_.set(a, _p, _.omit(_.get(tree, _p), 'children'));
}
});
deleteNullNode(a);
return a;
};
使用:
shake(tree, leaf => leaf.id == 5 )
算法还有很多改进的地方,有些内置的处理需要抽象。后续再更新。