From 663165abfcbc248b6070323cdd0c0368dd7fbd0d Mon Sep 17 00:00:00 2001 From: Jonathan Niles Date: Mon, 22 Jan 2018 16:13:24 +0100 Subject: [PATCH] feat(Tree): implement sumOnProperty() This commit implements the sumOnProperty() function to sum tree nodes on a given property. Parent nodes are given the values of the sums of their children. --- server/lib/Tree.js | 34 +++++++++++++++++++++++++---- test/server-unit/Tree.spec.js | 40 +++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/server/lib/Tree.js b/server/lib/Tree.js index fb1b1df957..c240803b22 100644 --- a/server/lib/Tree.js +++ b/server/lib/Tree.js @@ -59,20 +59,46 @@ function flatten(tree, depth, pruneChildren = true) { }, []); } +/** + * @function sumOnProperty + * + * @description + * Computes the value of all parent nodes in the tree as the sum of the values + * of their children for a given property. + */ +function sumOnProperty(node, prop) { + if (node.children.length > 0) { + // recursively compute the value of node[prop] by summing all child[prop]s + node[prop] = node.children.reduce((value, child) => + value + sumOnProperty(child, prop), 0); + } + + return node[prop]; +} class Tree { - constructor(data = [], parentKey = 'parent', rootId = 0) { + constructor(data = [], options = { + parentKey : 'parent', + rootId : 0, + }) { this._data = data; - this._parentKey = parentKey; - this._rootId = rootId; + + this._parentKey = options.parentKey; + this._rootId = options.rootId; // build the tree with the provided root id and parentKey - this._tree = buildTreeFromArray(_.cloneDeep(data), rootId, parentKey); + this._tree = buildTreeFromArray(_.cloneDeep(data), this._rootId, this._parentKey); } toArray() { return flatten(this._tree); } + + sumOnProperty(prop) { + this._tree.forEach(node => { + sumOnProperty(node, prop); + }); + } } module.exports = Tree; diff --git a/test/server-unit/Tree.spec.js b/test/server-unit/Tree.spec.js index ae9faf2268..dfea4b3beb 100644 --- a/test/server-unit/Tree.spec.js +++ b/test/server-unit/Tree.spec.js @@ -7,8 +7,8 @@ function TreeUnitTests() { * ROOT * / | \ * id:1 id:4 id:6 - * / \ - * id:2 id:3 + * / / \ + * id:2 id:7 id:3 * / * id:5 */ @@ -21,15 +21,26 @@ function TreeUnitTests() { }, { id : 3, parent : 6, + valueA : 10, + valueB : 2, }, { id : 4, parent : 0, + valueA : 30, + valueB : 4, }, { id : 5, parent : 2, + valueA : 9, + valueB : 7, }, { id : 6, parent : 0, + }, { + id : 7, + parent : 6, + valueA : 10, + valueB : 19, }]; it('#constructor() should populate private variables', () => { @@ -64,11 +75,11 @@ function TreeUnitTests() { expect(node4.children).to.have.length(0); }); - it('#constructor() node id:6 should have one child', () => { + it('#constructor() node id:6 should have two children', () => { const tree = new Tree(nodes)._tree; const node6 = tree[2]; expect(node6.id).to.equal(6); - expect(node6.children).to.have.length(1); + expect(node6.children).to.have.length(2); }); it('#toArray() should return an array', () => { @@ -82,6 +93,27 @@ function TreeUnitTests() { expect(node).to.have.property('depth'); }); }); + + it('#sumOnProperty() should compute the balances of level 1 nodes', () => { + const tree = new Tree(nodes); + const subTree = tree._tree; + + // first level should not be defined + expect(subTree[0].valueA).to.be.undefined; + expect(subTree[0].valueB).to.be.undefined; + + // this is a level 1 leaf node, so its values are known. + expect(subTree[1].valueA).to.equal(30); + expect(subTree[1].valueB).to.equal(4); + + tree.sumOnProperty('valueA'); + expect(subTree[0].valueA).to.equal(9); + expect(subTree[0].valueB).to.be.undefined; + + // this condition sums multiple leaves + tree.sumOnProperty('valueB'); + expect(subTree[2].valueB).to.equal(21); // 2 + 19 + }); } describe('Tree.js', TreeUnitTests);