I have a deeply nested javascript object with an unlimited amout of children. Every child has a value and a totalValue. The totalValue should be the sum of all the values from all its children and subchildren. How can I make this work?
At the moment I'm only able to loop the whole object using a recursive function:
// Recursive function
_.each(names, function(parent) {
if(parent.children.length > 0) {
recursiveFunction(parent.children);
}
});
function recursiveFunction(children){
_.each(children, function(child) {
if(child.children.length > 0) {
recursiveFunction(child.children)
}
});
};
// Deeply nested javascript object
var names = {
name: 'name-1',
value: 10,
valueTotal: 0, // should be 60 (name-1.1 + name-1.2 + name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2',
value: 10,
valueTotal: 0, // should be 40 (name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.2.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2.2',
value: 10,
valueTotal: 0, // should be 20 (name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.2.2.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2.2.2',
value: 10,
valueTotal: 0,
children: []
}]
}]
}]
}
}
I have a deeply nested javascript object with an unlimited amout of children. Every child has a value and a totalValue. The totalValue should be the sum of all the values from all its children and subchildren. How can I make this work?
At the moment I'm only able to loop the whole object using a recursive function:
// Recursive function
_.each(names, function(parent) {
if(parent.children.length > 0) {
recursiveFunction(parent.children);
}
});
function recursiveFunction(children){
_.each(children, function(child) {
if(child.children.length > 0) {
recursiveFunction(child.children)
}
});
};
// Deeply nested javascript object
var names = {
name: 'name-1',
value: 10,
valueTotal: 0, // should be 60 (name-1.1 + name-1.2 + name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2',
value: 10,
valueTotal: 0, // should be 40 (name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.2.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2.2',
value: 10,
valueTotal: 0, // should be 20 (name-1.2.2.1 + name-1.2.2.2)
children: [{
name: 'name-1.2.2.1',
value: 10,
valueTotal: 0,
children: []
}, {
name: 'name-1.2.2.2',
value: 10,
valueTotal: 0,
children: []
}]
}]
}]
}
}
Share
Improve this question
edited Jan 19, 2017 at 16:10
Sam
asked Jan 19, 2017 at 15:36
SamSam
1,3433 gold badges24 silver badges41 bronze badges
2
- 1 your sum looks wrong, name-2 is children of name-1, and should be included. – Nina Scholz Commented Jan 19, 2017 at 16:01
- 1 Your right! i'll change it. – Sam Commented Jan 19, 2017 at 16:02
4 Answers
Reset to default 7So in fact you wanna do sth like this: every elem asks his childs for its values, these do the same and give back their totalValues plus their own value.
function sumUp(object){
object.totalValue = 0;
for(child of object.children){
object.totalValue += sumUp(child);
}
return object.totalValue + object.value;
}
Start like this:
const totalofall = sumUp(names);
console.log(names); //your expected result.
Working example: http://jsbin./laxiveyoki/edit?console
You could use plain Javascript with an iterative and recursive approach and some sanity checks for local totals.
function calculateValues(o) {
o.valueTotal = (o.children || []).reduce(function (r, a) {
calculateValues(a);
return r + (a.value || 0) + (a.valueTotal || 0);
}, 0);
}
var names = { name: 'name-1', value: 10, valueTotal: 0, children: [{ name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 0, children: [{ name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 0, children: [{ name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] }] }] }] };
calculateValues(names);
console.log(names);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Not a big fan of reinventing the wheel and would suggest to use a library for readability and maintainability. We use object-scan now for most of our data processing. It is quite versatile once you wrap your head around how to use it. Anyways, here is a possible solution for your question
// const objectScan = require('object-scan');
const injectSums = (data) => {
objectScan(['**.children'], {
filterFn: ({ parent, value: children }) => {
parent.valueTotal = children
.map(({ valueTotal, value }) => valueTotal + value)
.reduce((a, b) => a + b, 0);
}
})(data);
};
const names = { name: 'name-1', value: 10, valueTotal: 0, children: [ { name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 0, children: [ { name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 0, children: [ { name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] } ] } ] }] };
injectSums(names);
console.log(names);
// => { name: 'name-1', value: 10, valueTotal: 60, children: [ { name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 40, children: [ { name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 20, children: [ { name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] } ] } ] } ] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
Disclaimer: I'm the author of object-scan
This works for me. It adds previous count value in object to total count. This can be subtracted later
function sumUpTree(obj, key, totalKey, childrenKey) {
let sumUpTreeInner = (obj) => {
obj[totalKey] =
obj[key] +
(obj[childrenKey] || []).reduce(function (r, child) {
sumUpTreeInner(child);
return r + (child[totalKey] || 0);
}, 0);
};
sumUpTreeInner(obj);
}
This is inspired from above answers of Jonas Wilms and Nina Scholz