diff --git a/data.json b/data/csv.js similarity index 99% rename from data.json rename to data/csv.js index 4a4fd27..f24ed1a 100644 --- a/data.json +++ b/data/csv.js @@ -1,4 +1,4 @@ -[ +var CSV = [ ["fspr","plnt","promo","terms","bill_cust_descr","ship_cust_descr","dsm","quota_rep_descr","director","billto_group","shipto_group","chan","chansub","chan_retail","part","part_descr","part_group","branding","majg_descr","ming_descr","majs_descr","mins_descr","segm","substance","fs_line","r_currency","r_rate","c_currency","c_rate","units","value_loc","value_usd","cost_loc","cost_usd","calc_status","flag","order_date","order_month","order_season","request_date","request_month","request_season","ship_date","ship_month","ship_season","version","iter","logid","tag","comment","module"], ["2102","152","NONE","1A","BWIC0001 - BWI COMPANIES INC","BLOO0017 - BLOOMING COLOR","10032","BRYAN HILL","Baggetta","BWI","BLOOMING COLOR","DRP","DRP","DRP","HZP3E100G18D050","HZP3E100G18D050 - E-10 3 STRAND HGR BLACK","HZP3E100","","110 - INJECTION","000 - NON BRANDED","108 - ACCESSORIES","A06 - HANGERS","Greenhouse","Plastic","41010","US","1.0000000000","US","1.0000000000","25600.0000000000","2099.2000000000","2099.20","1697.2800000000","1697.28","CLOSED","SHIPMENT","2020-06-08","01 - Jun","2021","2020-06-15","01 - Jun","2021","2020-07-09","02 - Jul","2021","15mo","actuals","1","Initial Build","don't undo","build_pool"], ["2211","152","NONE","1A","PAST0002 - PASTANCH LLC","PAST0002 - PASTANCH LLC","13028","RICHARD MEULE","Soltis","PASTANCH LLC","PASTANCH LLC","WHS","WHS","WHS","AMK12000G18D050","AMK12000G18D050 - 3G 1200 REG BM POT BLACK","AMK12000","","210 - BLOW MOLD","000 - NON BRANDED","104 - ROUND POTS AND TRAYS","A19 - NURSERY POTS >= 1 GAL","Nursery","Plastic","41010","US","1.0000000000","US","1.0000000000","24000.0000000000","9144.0000000000","9144.00","4936.0800000000","4936.08","OPEN","REMAINDER","2022-03-26","10 - Mar","2022","2022-04-28","11 - Apr","2022","2022-04-28","11 - Apr","2022","b22","copy","1","Initial Build","don't undo","build_pool"], diff --git a/sample.csv b/data/sample.csv similarity index 100% rename from sample.csv rename to data/sample.csv diff --git a/index.html b/index.html index 391f105..5ca13ce 100755 --- a/index.html +++ b/index.html @@ -1,886 +1,491 @@ -
- - - - - + + + + + +
+ + + + - - - - - - - + + let ElRoot = props => + { + let pivots = N.Step(Pivot.Root, "Pivot")||[]; + return h("div", null, [ + h(PivotForm), + pivots.map(pivot=>h(PivotRoot, {key:pivot.Meta.Label, pivot})) + ]) + }; + const Render = () => render(h(ElRoot), document.querySelector("#app")); + Render(); + + + + diff --git a/libraries/n.js b/libraries/n.js new file mode 100644 index 0000000..7813650 --- /dev/null +++ b/libraries/n.js @@ -0,0 +1,172 @@ +var N = +{ +ID:{ + Walk:0, + Instance:0 +}, +Create(inMeta) +{ + return { + ID:{ + Walk:0, + Instance:N.ID.Instance++ + }, + Meta:inMeta||{}, + Link:{} + }; +}, +Connect(inNodeMajor, inNodeMinor, inKey, inUnique) +{ + if(inUnique) // bail if the nodes are already connected + { + let check = N.Step(inNodeMajor, inKey, true); + if(check) + { + if(check.indexOf(inNodeMinor) !== -1) + { + return; + } + } + } + N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); + N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); +}, +Disconnect(inNodeMajor, inNodeMinor, inKey) +{ + let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); + + // if no specific child was passed + if(inNodeMinor === null) + { + // get all the children + let check = N.Step(inNodeMajor, inKey); + if(!check){ return; } + + // go down to each child ... + check.forEach( inNodeMinor => + { + let connections = inNodeMinor.Link[inKey]; + remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent + + // if after the remove operation, this child has no connections on inKey, scrub the key + if(!connections.Set.length && !connections.Get.length) + { + delete inNodeMinor.Link[inKey]; + } + }); + + // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well + if(inNodeMajor.Link[inKey].Get.length == 0) + { + delete inNodeMajor.Link[inKey]; + } + return; + } + + // if no specific parent was passed + if(inNodeMajor === null) + { + // get all the parents + let check = N.Step(inNodeMinor, inKey, false); + if(!check){ return; } + + // go up to each parent ... + check.forEach( inNodeMajor => + { + let connections = inNodeMajor.Link[inKey]; + remove( connections.Set, inNodeMinor); // ... and remove any reference to the child + + // if after the remove operation, this parent has no connections on inKey, scrub the key + if( !connections.Set.length && !connections.Get.length ) + { + delete inNodeMajor.Link[inKey]; + } + }); + + // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well + if(inNodeMinor.Link[inKey].Set.length == 0) + { + delete inNodeMinor.Link[inKey]; + } + return; + } + + // if a specific parent and child were passed + if(inNodeMajor.Link[inKey].Set.length == 1) + { + delete inNodeMajor.Link[inKey]; + } + else + { + remove(inNodeMajor.Link[inKey].Set, inNodeMinor); + } + if(inNodeMinor.Link[inKey].Get.length == 1) + { + delete inNodeMinor.Link[inKey]; + } + else + { + remove(inNodeMinor.Link[inKey].Get, inNodeMajor); + } + +}, +Step(inNode, inKey, inForward, inForceCreate) +{ + let connectionGroup = inNode.Link[inKey]; + if(!connectionGroup) + { + if(inForceCreate === true) + { + inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; + } + else + { + return false; + } + } + return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; + +}, +Walk(inIterator, inNode, inKey, inForward, inTerminal) +{ + let array = N.Step(inNode, inKey, inForward); + + if(!array.length && inTerminal) + { + return inTerminal(inNode); + } + + for(let i=0; icolumn.Meta.Index); + console.log(numeric); + Pivot.Leaves = inRows.map(r => + { + numeric.forEach(index => r[index] = parseFloat(r[index])||0); + return N.Create({Row:r}); + } + ); + Pivot.Init = ()=>{}; +}, +Pivot(inRoot, inParent, inPivotIndicies, inSumIndicies, inDepth) +{ + //arguments: + // - a Node with leaf Nodes temporarily stored in its Meta.Leaves + // - where each leaf Node has a row of table data in it's Meta.Row + // - a list of columns to pivot on + // - a list of columns to sum + // - optional traversal depth, defaults to 0 + let depth = inDepth||0; + let uniques = {}; + let indexPivot = inPivotIndicies[depth]; + inParent.Meta.Leaves.forEach((inLeaf)=> + { + let row = inLeaf.Meta.Row; // shorthand for the raw "CSV" row in the leaf Node's Meta + let value = row[indexPivot]; // get the pivot column + let match = uniques[value]; // check in the uniques list if this pivot column exists + if(!match) + { + // if not, store a value under that key that will be the meta object for a new child + + let clone = row.map(r=>null); + inSumIndicies.forEach((inSumIndex, inIndex, inArray)=> + { + clone[inSumIndex] = row[inSumIndex] + }); + match = uniques[value] = { + Label:value, + Row:clone, + IndexPivot:indexPivot, + IndexSum:inSumIndicies, + Leaves:[], + Depth:depth + }; + // grow a child off of the parent using the meta object + N.Connect(inParent, N.Create(match), "Hierarchy"); + } + else + { + // if a match does exist, sum the appropriate columns + inSumIndicies.forEach((inSumIndex) => match.Row[inSumIndex] += row[inSumIndex]); + } + // move the leaves into the child + match.Leaves.push(inLeaf); + }); + + // get the leaves out of the parent, at this point they have been re-distributed to the children + delete inParent.Meta.Leaves; + let iterator = () => {}; + if(depth >= inPivotIndicies.length-1) + { + iterator = inLastBranch => + { + inLastBranch.Meta.Leaves.forEach( inLeaf => + { + let modifiers = []; + let collectModifier = n => modifiers.push(n); + let connectModifiers = n => modifiers.forEach(inModifier => N.Connect(inModifier, n, "ModifyOut", true)); + + // collect modifiers effecting leaves + N.Walk(collectModifier, inLeaf, "ModifyAt", false); + N.Walk(collectModifier, inLeaf, "ModifyDown", false); + + if(modifiers.length) + { + // apply them to the branch + inLastBranch.ID.Walk = N.ID.Walk; + connectModifiers(inLastBranch); + + // also walk them up and connect to parents, but with "check unique" enabled + console.log("walking modifiers up from", inLastBranch.Meta.Label); + N.ID.Walk++; + N.Walk(connectModifiers, inLastBranch, "Hierarchy", false); + } + + // lastly connect the leaf to the branch + N.Connect(inLastBranch, inLeaf, "Leaf"); + + }); + delete inLastBranch.Meta.Leaves; + return false; + } + } + else + { + iterator = child => { + Pivot.Pivot(inRoot, child, inPivotIndicies, inSumIndicies, depth+1); + return false; + }; + } + N.Walk(iterator, inParent, "Hierarchy"); + + return inParent; +}, +Create(inLabel, inPivotIndicies, inSumIndicies) +{ + N.ID.Walk++; + + /* + let sumColumns = (N.Step(Pivot.Schema, "sum")||[]).map(column=>column.Meta.Index); + let labelColumns = (N.Step(Pivot.Schema, "label")||[]).map(column=>column.Meta.Index); + */ + + let pivotRoot = N.Create({Label:inLabel, Leaves:Pivot.Leaves}); + N.Connect(Pivot.Root, pivotRoot, "Pivot"); + return Pivot.Pivot(pivotRoot, pivotRoot, inPivotIndicies, inSumIndicies); +}, +Delete(inRoot) +{ + // disconnect modifiers + let check = N.Step(inRoot, "ModifyUp", false); + if(check) + { + while(check.length>0) + { + Pivot.Unmodify(check[0]); + } + } + + // disconnect leaves from terminal branches + N.Walk(()=>{}, inRoot, "Hierarchy", true, terminal=>{ + N.Disconnect(terminal, null, "Leaf"); + }); + + // disconnect from app + N.Disconnect(null, inRoot, "Pivot"); +}, +Modify(inNode) +{ + let modified = N.Create({Label:"Modifier"}); + + // traverse + let gatherUp = n => N.Connect(modified, n, "ModifyUp"); + let gatherDown = n => N.Connect(modified, n, "ModifyDown"); + let gatherOut = n => N.Connect(modified, n, "ModifyOut"); + + N.ID.Walk++; + inNode.ID.Walk = N.ID.Walk; + + // at + N.Connect(modified, inNode, "ModifyAt"); + + // up + N.Walk(gatherUp, inNode, "Hierarchy", false); + + // down 1 + N.Walk(gatherDown, inNode, "Hierarchy", true, terminal=> + { + // down 2 + // for each terminal node, step down into its leaves and gather down + N.Walk(gatherDown, terminal, "Leaf", true, leaf=> + { + // out 1 + // walk back up on the leaf connections on other trees + N.Walk(gatherOut, leaf, "Leaf", false, terminal=> + { + // out 2 + // and continueup the hierarchy + N.Walk(gatherOut, terminal, "Hierarchy", false); + }); + }); + }); + + return modified; +}, +Unmodify(inModifier) +{ + N.Disconnect(inModifier, null, "ModifyUp"); + N.Disconnect(inModifier, null, "ModifyDown"); + N.Disconnect(inModifier, null, "ModifyOut"); + N.Disconnect(inModifier, null, "ModifyAt"); + N.Disconnect(null, inModifier, "Modifier"); +} +}; \ No newline at end of file diff --git a/save.json b/save.json deleted file mode 100644 index 0ecad77..0000000 --- a/save.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name":"", - "csv":["data/output/1.csv", "data/output/2.csv"], - "pivots": - [ - { - "pivot":[0, 1], - "sum":[3], - "modifications": - [ - { - "path":[0, 0], - "modification":[0.2] - } - ] - } - ] - } -] \ No newline at end of file