shuffle folder structure
This commit is contained in:
		
							parent
							
								
									324b3c22cb
								
							
						
					
					
						commit
						852bfcaf0c
					
				| @ -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"], | ["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"], | ["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"], | ["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"], | ||||||
							
								
								
									
										431
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										431
									
								
								index.html
									
									
									
									
									
								
							| @ -1,412 +1,18 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"/> | ||||||
|  |     <script src="./libraries/papaparse.min.js"></script> | ||||||
|  |     <script src="./libraries/n.js"></script> | ||||||
|  |     <script src="./libraries/pivot.js"></script> | ||||||
|  |     <script src="./data/csv.js"></script> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|     <div id="app"></div> |     <div id="app"></div> | ||||||
| 
 |     <!-- initialize table --> | ||||||
| <script src="papaparse.min.js"></script> |  | ||||||
| 
 |  | ||||||
| <!-- N --> |  | ||||||
|     <script> |     <script> | ||||||
| var N = |     let columnNames = CSV.shift(); | ||||||
| { |  | ||||||
|     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; i<array.length; i++) |  | ||||||
|         { |  | ||||||
|             let next = array[i]; |  | ||||||
|             if(next.ID.Walk !== N.ID.Walk) |  | ||||||
|             { |  | ||||||
|                 next.ID.Walk = N.ID.Walk; |  | ||||||
|                 //console.log("processing", next.Meta) |  | ||||||
|                 let results = inIterator(next); |  | ||||||
|                 if(results !== false) |  | ||||||
|                 { |  | ||||||
|                     N.Walk(inIterator, next, inKey, inForward, inTerminal); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     //console.log("routine exited"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 //console.log("id collision"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     Path(inArray, inNode, inKey, inForward) |  | ||||||
|     { |  | ||||||
|         var current = inNode; |  | ||||||
|         var direction = inForward||true; |  | ||||||
|         for(let i=0; i<inArray.length; i++) |  | ||||||
|         { |  | ||||||
|             current = N.Step(current, inKey, direction)[inArray[i]]; |  | ||||||
|         } |  | ||||||
|         return current; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <!-- Pivot --> |  | ||||||
| <script> |  | ||||||
| var Pivot =  |  | ||||||
| { |  | ||||||
|     Leaves:{}, |  | ||||||
|     Root:N.Create({Label:"All Pivots"}), |  | ||||||
|     Schema:N.Create({Label:"Column Details"}), |  | ||||||
|     Proto:N.Create({Label:"User Form"}), |  | ||||||
|     Init(inColumnTypes, inColumnNames, inRows) |  | ||||||
|     {         |  | ||||||
|         for(let i=0; i<inColumnNames.length; i++) |  | ||||||
|         { |  | ||||||
|             let columnNode = N.Create({Label:inColumnNames[i], Index:i}); |  | ||||||
|             N.Connect(Pivot.Schema, columnNode, inColumnTypes[i]); |  | ||||||
|             N.Connect(Pivot.Schema, columnNode, "all"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let numeric = (N.Step(Pivot.Schema, "sum")||[]).map(column=>column.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"); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <!-- rendering --> |  | ||||||
| <script type="module"> |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| Pivot.Init( |  | ||||||
|     ["id", "type-a", "type-b", "count", "extra"], |  | ||||||
|     ["label", "label", "label", "sum", "sum"], |  | ||||||
|     [ |  | ||||||
|     ["#1", "a", "long",  1, 4], |  | ||||||
|     ["#2", "b", "long",  2, 4], |  | ||||||
|     ["#3", "b", "short", 2, 4], |  | ||||||
|     ["#4", "a", "long",  3, 4], |  | ||||||
|     ["#5", "b", "short", 1, 4], |  | ||||||
|     ["#6", "a", "short", 0, 4], |  | ||||||
|     ["#7", "b", "short", 7, 4] |  | ||||||
|     ] |  | ||||||
| ); |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| Papa.parse("./sample.csv", |  | ||||||
| { |  | ||||||
|     download:"true", |  | ||||||
| 	complete: function(results) |  | ||||||
|     { |  | ||||||
|         let columnNames = results.data.shift(); |  | ||||||
|     let columnTypes = ([...columnNames]).fill("hidden"); |     let columnTypes = ([...columnNames]).fill("hidden"); | ||||||
| 
 |  | ||||||
|     columnTypes[29] = "sum"; |     columnTypes[29] = "sum"; | ||||||
|     columnTypes[30] = "sum"; |     columnTypes[30] = "sum"; | ||||||
|     columnTypes[31] = "sum"; |     columnTypes[31] = "sum"; | ||||||
| @ -417,16 +23,14 @@ Papa.parse("./sample.csv", | |||||||
|     columnTypes[9 ] = "label"; |     columnTypes[9 ] = "label"; | ||||||
|     columnTypes[10] = "label"; |     columnTypes[10] = "label"; | ||||||
|     columnTypes[11] = "label"; |     columnTypes[11] = "label"; | ||||||
| 
 |  | ||||||
|     Pivot.Init( |     Pivot.Init( | ||||||
|         columnTypes, |         columnTypes, | ||||||
|         columnNames, |         columnNames, | ||||||
|             results.data |         CSV | ||||||
|     ); |     ); | ||||||
|         Render(); |     </script> | ||||||
| 	} |     <!-- rendering --> | ||||||
| }); |     <script type="module"> | ||||||
| 
 |  | ||||||
|     import { h, render, createContext, Fragment } from 'https://cdn.skypack.dev/preact'; |     import { h, render, createContext, Fragment } from 'https://cdn.skypack.dev/preact'; | ||||||
|     import { useReducer, useState } from 'https://cdn.skypack.dev/preact/hooks'; |     import { useReducer, useState } from 'https://cdn.skypack.dev/preact/hooks'; | ||||||
|     import { css, cx } from 'https://cdn.skypack.dev/@emotion/css'; |     import { css, cx } from 'https://cdn.skypack.dev/@emotion/css'; | ||||||
| @ -883,4 +487,5 @@ const Render = () => render(h(ElRoot), document.querySelector("#app")); | |||||||
|     Render(); |     Render(); | ||||||
|      |      | ||||||
|     </script> |     </script> | ||||||
| 
 | </body> | ||||||
|  | </html> | ||||||
|  | |||||||
							
								
								
									
										172
									
								
								libraries/n.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								libraries/n.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; i<array.length; i++) | ||||||
|  |     { | ||||||
|  |         let next = array[i]; | ||||||
|  |         if(next.ID.Walk !== N.ID.Walk) | ||||||
|  |         { | ||||||
|  |             next.ID.Walk = N.ID.Walk; | ||||||
|  |             //console.log("processing", next.Meta)
 | ||||||
|  |             let results = inIterator(next); | ||||||
|  |             if(results !== false) | ||||||
|  |             { | ||||||
|  |                 N.Walk(inIterator, next, inKey, inForward, inTerminal); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 //console.log("routine exited");
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             //console.log("id collision");
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }, | ||||||
|  | Path(inArray, inNode, inKey, inForward) | ||||||
|  | { | ||||||
|  |     var current = inNode; | ||||||
|  |     var direction = inForward||true; | ||||||
|  |     for(let i=0; i<inArray.length; i++) | ||||||
|  |     { | ||||||
|  |         current = N.Step(current, inKey, direction)[inArray[i]]; | ||||||
|  |     } | ||||||
|  |     return current; | ||||||
|  | } | ||||||
|  | }; | ||||||
							
								
								
									
										198
									
								
								libraries/pivot.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								libraries/pivot.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | |||||||
|  | var Pivot =  | ||||||
|  | { | ||||||
|  | Leaves:{}, | ||||||
|  | Root:N.Create({Label:"All Pivots"}), | ||||||
|  | Schema:N.Create({Label:"Column Details"}), | ||||||
|  | Proto:N.Create({Label:"User Form"}), | ||||||
|  | Init(inColumnTypes, inColumnNames, inRows) | ||||||
|  | {         | ||||||
|  |     for(let i=0; i<inColumnNames.length; i++) | ||||||
|  |     { | ||||||
|  |         let columnNode = N.Create({Label:inColumnNames[i], Index:i}); | ||||||
|  |         N.Connect(Pivot.Schema, columnNode, inColumnTypes[i]); | ||||||
|  |         N.Connect(Pivot.Schema, columnNode, "all"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let numeric = (N.Step(Pivot.Schema, "sum")||[]).map(column=>column.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"); | ||||||
|  | } | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user