數據庫存儲結構爲平鋪的結構:javascript
[ {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}, ]
須要轉換成樹狀結構:java
{ children:[ { id: 1, name: '' }, { id: 2, name: '', children: [ { id: 3 }, { id: 4, children: [ { id: 5 } ] } ] }, ] }
算法以下:node
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 )
算法還有不少改進的地方,有些內置的處理須要抽象。後續再更新。code