<div id="app"></div> <script type="module"> import { h, Component, render } from 'https://unpkg.com/preact?module'; var N = { Create:(inMeta, ...inChildren) => { var output = { Meta:inMeta, Children:[], Parents:[], /* WalkID:0, GetUpward:[], GetDownward:[], GetOutside:[], SetUpward:[], SetDownward:[], SetOutside:[] */ }; inChildren.forEach( inChild => N.Connect(output, "Children", inChild, "Parents") ); return output; }, Connect:(inParent, inParentRefs, inChild, inChildRefs) => { inParent[inParentRefs].push(inChild); inChild[inChildRefs].push(inParent); }, Disconnect:(inParent, inParentRefs, inChild, inChildRefs) => { let checkRemove = (inArray, inMember) => { inArray.findIndex( (inMember, inIndex, inArray) => (inMember === match) ? inArray.splice(inIndex, 1) : false ); }; checkRemove(inParent[inParentRefs], inChild); checkRemove(inChild[inChildRefs], inParent); }, Walk:(inNode, inKey, inIterator, inWalkID) => { let array = inNode[inKey]; for(let i=0; i<array.length; i++) { let next = array[i]; if(next.WalkID !== inWalkID) { next.WalkID = inWalkID; let results = inIterator(next); if(results !== false) { N.Walk(next, inKey, inIterator, inWalkID); } } } }, Modify(inNode) { inNode.WalkID = "tweak-"+Math.random(); let leaves = []; let gatherUp = n => N.Connect(inNode, "SetUpward", n, "GetUpward"); let gatherDown = n => { N.Connect(inNode, "SetDownward", n, "GetDownward"); n.Children.length == 0 ? leaves.push(n) : null; }; let gatherOut = n => N.Connect(inNode, "SetOutside", n, "GetOutside"); N.Walk(inNode, "Parents", gatherUp, inNode.WalkID); N.Connect(inNode, "SetDownward", inNode, "GetDownward"); N.Walk(inNode, "Children", gatherDown, inNode.WalkID); leaves.forEach(leaf=>N.Walk(leaf, "Parents", gatherOut, inNode.WalkID)); } // delete modify // create pivot // delete pivot }; let Leafify = inRows => inRows.map(r => N.Create({Row:r})); let Pivot = (inParent, inColumnIndicies, inSumIndicies, inDepth) => { let depth = inDepth||0; let uniques = {}; let columnIndex = inColumnIndicies[depth]; inParent.Meta.Leaves.forEach((inLeaf)=> { let row = inLeaf.Meta.Row; let value = row[columnIndex]; let match = uniques[value]; if(!match) { match = uniques[value] = {Label:value, Row:[...row], Leaves:[]}; N.Connect(inParent, "Children", N.Create(match), "Parents"); } else { inSumIndicies.forEach(inIndex => match.Row[inIndex] += row[inIndex]); } match.Leaves.push(inLeaf); }); delete inParent.Meta.Leaves; if(depth == inColumnIndicies.length-1) { // cant go any deeper inParent.Children.forEach( inChild => { inChild.Meta.Leaves.forEach( inLeaf => N.Connect(inChild, "Children", inLeaf, "Parents") ); delete inChild.Meta.Leaves; }); } else { inParent.Children.forEach( child => Pivot(child, inColumnIndicies, inSumIndicies, depth+1) ); } }; let csv = Leafify([ ["#1", "a", "long", 1], ["#2", "b", "long", 2], ["#3", "b", "short", 2], ["#4", "a", "long", 3], ["#5", "b", "short", 1], ["#6", "a", "short", 0], ["#7", "b", "short", 7], ]); let pivotRoot = N.Create({Leaves:csv}); Pivot(pivotRoot, [1, 2], [3]); console.log(pivotRoot); let ElNode = ({node}) => { var children = []; var table = []; if(node.Children.length) { if(node.Meta.Row) { table = node.Meta.Row.map( cell => h("span", {style:{padding:"10px"}}, cell)); } children = [ h("strong", null, node.Meta.Label||"a node"), ...table, ...node.Children.map( inChild => h(ElNode, {node:inChild})) ]; } else { children = [ h("strong", null, node.Meta.Label||"a node"), ...node.Meta.Row.map( cell => h("span", {style:{padding:"10px"}}, cell)) ]; } return h("div", {style:{padding:"10px"}}, children); } let ElRoot = ({root}) => { return h("div", null, [ h("h3", null, "tree view"), h(ElNode, {node:root}) ]) }; render(h(ElRoot, {root:pivotRoot}, null), document.querySelector("#app")); </script> <!--= <script> let tree1 = N.Create("root1", N.Create("branch1", N.Create("leaf1"), N.Create("leaf2"), N.Create("leaf3"), ), N.Create("branch2", N.Create("leaft3"), N.Create("leaft4") ) ); let leaves = []; let leavesCollect = n => { if(n.Children.length == 0) { leaves.push(n); } }; N.Walk(tree1, "Children", leavesCollect, 1); let tree2 = N.Create("root2", N.Create("branch3", N.Create("leaf5"), N.Create("leaf6") ), N.Create("branch4", ...leaves) ); let orchard = N.Create("orchard", tree1, tree2); /*************************************************************/ N.Modify(tree1); console.log(tree1); </script> -->