letcompany={sales:[{name:'John',salary:1000,},{name:'Alice',salary:1600,},],development:{sites:[{name:'Peter',salary:2000,},{name:'Alex',salary:1800,},],internals:[{name:'Jack',salary:1300,},],},};constaddEmployeeSalary=(sum,employee)=>sum+employee.salary;constsumEmployeeSalaries=(employees,total)=>employees.reduce(addEmployeeSalary,total);// Body recursive solutionfunctionsumSalaries(department,total=0){// base case - works on leaf nodes; a list of employeesif(Array.isArray(department))returnsumEmployeeSalaries(department,total);// recursive case - edge nodes lead to sub departmentsfor(letsubdepofObject.values(department)){total=sumSalaries(subdep,total);// recursively call for subdepartments, sum the results}returntotal;}console.log(sumSalaries(company));// 7700

Convert to a tail recursive solution

// prime recursionconstsumSalaries=(department)=>sum([department],0,0);functionsum(todo,i,total){// base case - nothing left to doif(i>=todo.length)returntotal;// recursive caseconstcurrent=todo[i];if(Array.isArray(current)){// works on a leaf node; a list of employeestotal=sumEmployeeSalaries(current,total);}else{// edge node leads to sub departmentstodo.push(...Object.values(current));}returnsum(todo,i+1,total);}

Introduce a one-shot loop around the function body

functionsum(todo,i,total){while(true){// base case - nothing left to doif(i>=todo.length)returntotal;// recursive caseconstcurrent=todo[i];if(Array.isArray(current)){// works on a leaf node; a list of employeestotal=sumEmployeeSalaries(current,total);}else{// edge node leads to sub departmentstodo.push(...Object.values(current));}returnsum(todo,i+1,total);break;}}

Replace all recursive tail calls f(x=x1, y=y1, ...) with [x, y, ...] = [x1, y1, ...]; continue;

functionsum(todo,i,total){while(true){// base case - nothing left to doif(i>=todo.length)returntotal;// recursive caseconstcurrent=todo[i];if(Array.isArray(current)){// works on a leaf node; a list of employeestotal=sumEmployeeSalaries(current,total);}else{// edge node leads to sub departmentstodo.push(...Object.values(current));}i+=1;continue;break;}}

Tidy up

functionsum(todo,i,total){while(i<todo.length){constcurrent=todo[i];if(Array.isArray(current)){// works on a leaf node; a list of employeestotal=sumEmployeeSalaries(current,total);}else{// edge node leads to sub departmentstodo.push(...Object.values(current));}i+=1;}returntotal;}

Make more idiomatic

functionsumSalaries(department){lettotal=0;for(lettodo=[department],i=0;i<todo.length;i+=1){constcurrent=todo[i];if(!Array.isArray(current)){// edge node leads to sub departmentstodo.push(...Object.values(current));continue;}// works on a leaf node; a list of employeestotal=sumEmployeeSalaries(current,total);}returntotal;}

But why bother with recursion in JavaScript, especially when you can blow the stack?

Because it allows you to arrive at simple recursive solutions quickly which can then be transformed to

clean iterationwith a recipe.Example: JSFiddle

Start with a body recursive solution

Convert to a tail recursive solution

Introduce a one-shot loop around the function body

Replace all recursive tail calls

`f(x=x1, y=y1, ...)`

with`[x, y, ...] = [x1, y1, ...]; continue;`

Tidy up

Make more idiomatic

[ref]

Thank you for this, seeing a more in depth example in a more practical context helps a lot!