Search code examples
javascriptarraysloopsrecursioniteration

Recursively iterate through Object Array to sum package totals


I have an array of items and some items in the array are considered to be packages. The packages can be nested inside of each other to X number of levels. The packages are also able to be collapsed but the total dollar amount of the package at each level has to account for its children.

The way to determine the start of a package would be a BP (Begin Package), and the end of a package would be a EP (End Package). However if the package is collapsed, the EP item does not exist in the items array. However the totals of the packages need to be accounted for.

Stuck on trying to figure out a way to keep track of which package is nested in another package and how to total the nested packages and account for those nested totals in the parent total.

Data Array:

[
 { id: BP, collapsed: false },
 { id: 1 },
 { id: 2 },
 { id: BP, collapsed: false },
 { id: 3 },
 { id: BP, collapsed: true },
 { id: 4 },
 { id: EP },
 { id: EP }, 
]

Result nesting should be:
BP
1
2
   BP
   3
      BP
      // Collapsed Items
      5
      EP
   4
   EP
EP

id = item just means it is a regular item and it will be included in the package items when calculating the package totals, there are no properties included that help determine if it is inside a package or the start or end of a package. However It will contribute to the package sum when calculating totals.

The issue here is that I cant iterate over the items easily because the EP for the 3rd package does not exist, therefore I cant think of a way to go up to the 2nd level since I wont encounter an EP for that item to determine the end of the package. But I need to total the root package and include all the children, as well as total the child packages that will also include their children.

Right now the package are limited to nesting 3 levels deep however if possible I would like to support infinitely nesting items using recursion if possible.

Any help at all would be amazing, ive been staring at the same issue for days now. Thank you in advance!!!

Ive tried iterating using a for loop using the Bp and Ep items to keep track of level.

Ex.

var epNeeded = 0;
var nestedPkgNum = -1;
var pkgItems = [];
var nestedPkgLines = [][];

for (var i = 0, size = items.length, i < size; i++) {
 if (epNeeded === 0 && item.id === "BP") {
  if (!item.collapsed) {
   epNeeded++
  }
 }

 // If EP needed greater than 0, then it would be nested in root package
 else if (epNeeded > 0 && item.id === "BP") {
  nestedPkgNum++;
  
  if (!item.collapsed) {
   epNeeded++
  }
 }

 else if (item.id === "EP") {
  if (epNeeded > 1) {
   epNeeded--;
   // Sum child package
  }

  // End of root package
  if (epNeeded === 1) {
   epNeeded = 0;
   
   // Sum all package items
  }
 }
 
 else if (epNeeded > 1) {
  nestedPkgLines[nestedPkgNum].push(item)
 }

 else if (epNeeded === 1) {
  pkgLines.push(item)
 }
}

//Result:
// pkgItems
[
  {id: 1}, 
  {id: 2}
]

// nestedPkgLines
[
  0: [{id: 3}, {id: 4}],
  1: [{id: 5}]
]

The issue with result is that when I get to the 3rd level of nesting, it adds to nestedPkgLines as another array and therefore only counts for the root package total and not for the total of the 2nd level package as well.

I have gotten this to work for the 2nd level of nesting because I can keep track of items in a root package and a 2nd level nested package but I cant determine a way to keep track of the items in a 3rd level of nesting and associate them with their package so I can total the package and then use that package total in the sum for the root package.


Solution

  • I don't completely understand your question, but this answer might help you refine the question:

    const data = [{"id":"BP","collapsed":false},{"id":"item"},{"id":"item"},{"id":"BP","collapsed":false},{"id":"item"},{"id":"BP","collapsed":true},{"id":"item"},{"id":"EP"},{"id":"EP"}]
    
    const root = {}
    const stack = [root]
    data.forEach(({id,...o})=>{
      if(id==='BP') {
        let x = {id,...o};
        (stack[0].children??=[]).push(x)
        stack.unshift(x)
      }
      else if(id==='EP') stack.shift()
      else (stack[0].children??=[]).push({id,...o})
    })
    console.log(root.children)