how to calculate new data from objects in javascript
Basically this proposal uses a tree for the wanted values.
Generate a sort pattern for the right assignment of
names
property.Iterate the given data
- Get a copy of
a.names
. - Sort
names
. - Test if
relations.functions.total
contains the first element ofnames
, then unshift'total'
to names. - Iterate
names
and build an object based on the elements. - Assign
value
to thevalue
property in the object.
- Get a copy of
Calculate all missing values only for
result.total
branch.- This sums all single properties as well for the wanted items.
function calculateValues(o) { return Object.keys(o).reduce(function (r, k) { var v; if (k === 'value') { return r; } v = calculateValues(o[k]); if (o[k].value === null) { o[k].value = v; } values[k] = (values[k] || 0) + o[k].value; return r + o[k].value; }, 0);}var data = [{ names: ["a3", "printed", "black"], value: 15 }, { names: ["a3", "copied", "black"], value: 87 }, { names: ["a3", "printed", "color", "full"], value: 37 }, { names: ["a3", "copied", "color", "single"], value: 0 }, { names: ["a3", "copied", "color", "full"], value: 44 }, { names: ["a3", "scanned"], value: 288 }, { names: ["total"], value: 242142 }, { names: ["scanned"], value: 67411 }, { names: ["copied", "black"], value: 79997 }, { names: ["copied", "full", "color"], value: 809 }, { names: ["copied", "single", "color"], value: 0 }, { names: ["printed", "two", "color"], value: 0 }, { names: ["printed", "black"], value: 120665 }, { names: ["printed", "full", "color"], value: 40657 }], relations = { colors: { "black": "", color: ["full", "two", "single"] }, functions: { scanned: "", total: ["printed", "copied", "faxed"] }, papers: { "a3": "" } }, priorities = ['functions', 'colors', 'papers'], // as long as keys of objects are not ordered order = {}, result = {}, values = {}, i = 0;priorities.forEach(function (p) { Object.keys(relations[p]).forEach(function (k) { order[k] = ++i; Array.isArray(relations[p][k]) && relations[p][k].forEach(function (a) { order[a] = ++i; }); });});data.forEach(function (a) { var names = a.names.slice(); names.sort(function (a, b) { return (order[a] || 0) - (order[b] || 0); }); if (relations.functions.total.indexOf(names[0]) !== -1) { names.unshift('total'); } names.reduce(function (o, k) { return o[k] = o[k] || { value: null }; }, result).value = a.value;});calculateValues(result.total);// calculateCount(result.scanned); console.log(values);console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Below is the proof of concept. It is in CoffeeScript. You can easily compile it to JavaScript using js2.coffee. Else I have included the JS code for reference. Not sure if this is what you looking for. It probably isn't best approach but it may help you. Once you find (found), you can set your third (obj3) way you want.
###THIS IS COFFEESCRIPT BELOW###Array::containsAny = (arr) -> @some (v) -> arr.indexOf(v) >= 0obj1 = []obj2 = {}obj3 = {}totalArr = []colorArr = []bl = nullscan = nulla3 = nullfor k,v of obj2 colObj = v if k is 'colors' funcObj = v if k is 'functions' paperObj = v if k is 'papers' if colObj isnt null for k,v of colObj colorArr = v if k is 'color' bl = k if k 'black' if funcObj isnt null for k,v of funcObj totalArr = v if k is 'total' scan = k if k is 'scanned' if paperObj isnt null for k,v of paperObj a3 = k if k is 'a3' returnfor k,v of obj1 names = v if k is 'names' val = v if k is 'value' foundBlack = names.containsAny(['black']) founda3 = names.containsAny(['a3']) foundColor = names.containsAny(colorArr) foundTotal = names.containsAny(TotalArr) return
var a3, bl, colObj, colorArr, foundBlack, foundColor, foundTotal, founda3, funcObj, k, names, obj1, obj2, obj3, paperObj, scan, totalArr, v, val;Array.prototype.containsAny = function(arr) { return this.some(function(v) { return arr.indexOf(v) >= 0; });};//Your first array of objectsobj1 = [];//your second object of objectsobj2 = {};//declaring an empty objectobj3 = {};totalArr = [];colorArr = [];bl = null;scan = null;a3 = null;for (k in obj2) { v = obj2[k]; if (k === 'colors') { colObj = v; } if (k === 'functions') { funcObj = v; } if (k === 'papers') { paperObj = v; } if (colObj !== null) { for (k in colObj) { v = colObj[k]; if (k === 'color') { colorArr = v; } if (k('black')) { bl = k; } } } if (funcObj !== null) { for (k in funcObj) { v = funcObj[k]; if (k === 'total') { totalArr = v; } if (k === 'scanned') { scan = k; } } } if (paperObj !== null) { for (k in paperObj) { v = paperObj[k]; if (k === 'a3') { a3 = k; } } } return;}for (k in obj1) { v = obj1[k]; if (k === 'names') { names = v; } if (k === 'value') { val = v; } foundBlack = names.containsAny(['black']); founda3 = names.containsAny(['a3']); foundColor = names.containsAny(colorArr); foundTotal = names.containsAny(TotalArr); return;}
Do you need to calculate value
for each of names
? If yes, then try
var output = {};for (var i in data) { for (var j in data[i].names) { var mark = data[i].names[j]; output[mark] = (output[mark] || 0) + data[i].value; }}