Reputation: 169
I have the following 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
} ]
I tried to create some structure to organize the data in a way I can see relations between objects and calculate new objects. basically what I want is to be able to calculate missing data. So for example I know these relations:
{
"colors" : {
"black" : "",
"color" : [ "full", "two", "single" ]
},
"functions" : {
"scanned" : "",
"total" : [ "printed", "copied", "faxed" ]
},
"papers" : {
"a3" : ""
}
}
Based on this I would like to get the following:
{
"a3" : 183,
"color" : 41466,
"black" : 200662,
"copied" : 80806,
"printed" : 161322
}
I know it by taking into consideration the following: a3 total is only composed of the functions printed, copied and faxed so for example the a3 scanned value is not inside that calculation for the value of a3 total.
but I can't think of any idea how to do it using JavaScript. can anybody points me in the right direction?
Upvotes: 2
Views: 2103
Reputation: 386654
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
a.names
.names
.relations.functions.total
contains the first element of names
, then unshift 'total'
to names.names
and build an object based on the elements.value
to the value
property in the object.Calculate all missing values only for result.total
branch.
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; }
Upvotes: 22
Reputation: 655
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;
}
}
https://jsfiddle.net/chukanov/0kctjwyv/
Upvotes: 0
Reputation: 373
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) >= 0
obj1 = []
obj2 = {}
obj3 = {}
totalArr = []
colorArr = []
bl = null
scan = null
a3 = null
for 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'
return
for 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 objects
obj1 = [];
//your second object of objects
obj2 = {};
//declaring an empty object
obj3 = {};
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;
}
Upvotes: 0