I have a JSON array of objects (a collection) like:
[{
\"x\": {
\"x1\": 1
},
\"y\": {
\"yt\": 0,
\"zt\": 4,
\"qa\": 3,
\"ft\": 0,
You could first collect and sum all values in the same data structure and then calculkate the average by a division with the length of the given array.
function getParts(array, result) {
function iter(o, r) {
Object.keys(o).forEach(function (k) {
if (o[k] && typeof o[k] === 'object') {
return iter(o[k], r[k] = r[k] || {});
}
r[k] = (r[k] || 0) + o[k];
});
}
function avr(o) {
Object.keys(o).forEach(function (k) {
if (o[k] && typeof o[k] === 'object') {
return avr(o[k]);
}
o[k] = o[k] /data.length;
});
}
data.forEach(function (a) {
iter(a, result);
});
avr(result);
}
var data = [{ x: { x1: 1 }, y: { yt: 0, zt: 4, qa: 3, ft: 0, } }, { x: { x1: 5 }, y: { yt: 10, zt: 2, qa: 0, ft: 0, } }],
result = {};
getParts(data, result);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
let objectArray = [{
"x": {
"x1": 1
},
"y": {
"yt": 0,
"zt": 4,
"qa": 3,
"ft": 0,
}
},
{
"x": {
"x1": 5
},
"y": {
"yt": 10,
"zt": 2,
"qa": 0,
"ft": 0,
}
}];
function findAverage(array) {
let counter = {},
result = {},
i,
obj,
key,
subKey;
// Iterate through array
for (i = 0; i < array.length; i++) {
obj = array[i];
// Copy each key in array element to counter object
for (key in obj) {
counter[key] = counter[key] || {};
// Increment and keep count of key-values of counter based on values in array element
for (subKey in obj[key]) {
counter[key][subKey] = counter[key][subKey] || {total: 0, numElements: 0};
counter[key][subKey].total += obj[key][subKey];
counter[key][subKey].numElements += 1;
}
}
}
// Go back through counter to find average of all existing subkeys (based on incremented total and the number of elements recorded) and throw it into result object
for (key in counter) {
result[key] = result[key] || {};
for (subKey in counter[key]) {
result[key][subKey] = counter[key][subKey].total / counter[key][subKey].numElements;
}
}
return result;
}
console.log(findAverage(objectArray));
Not designed to be absolutely optimal, and copying objects can be done recursively without knowing in advance their structure, but I wanted to keep the steps as clear as possible.
Edited to allow testing as snippet. Had no idea you could even do that on SO!
var array = [{
"x": {
"x1": 1
},
"y": {
"yt": 0,
"zt": 4,
"qa": 3,
"ft": 0
}
},
{
"x": {
"x1": 5
},
"y": {
"yt": 10,
"zt": 2,
"qa": 0,
"ft": 0
}
}];
function aintob(){
var o = {};
var first = array[0],
second = array[1];
var result = {x:{},y:{}};
var each = function(letter, oa, ob){
var i,
letter = {};
for(i in oa){
letter[i] = (oa[i]+ob[i])/2;
}
return letter;
}
o.x = each("x", first.x, second.x);
o.y = each("y", first.y, second.y);
return o;
}
console.log(aintob());
You can merge the objects using the spread syntax and lodash's _.mergeWith()
.
When merging, if the 2nd parameter (b) is a number divide it by the number of items in the original array to get it's respective contribution to the total average. If the 1st parameter (a) is a number, just add it without dividing (to avoid dividing the sum multiple times), or add 0 if it's undefined.
I've added examples of 2 objects array, and 3 objects array.
const getAvg = (data) => _.mergeWith({}, ...data, (a, b) => {
if(_.isNumber(b)) {
return ((b || 0) / data.length) + (_.isNumber(a) ? (a || 0) : 0);
}
});
const data1 = [
{"x":{"x1":1},"y":{"yt":0,"zt":4,"qa":3,"ft":0}},
{"x":{"x1":5},"y":{"yt":10,"zt":2,"qa":0,"ft":0}}
];
const data2 = [
{"x":{"x1":1},"y":{"yt":0,"zt":4,"qa":3,"ft":0}},
{"x":{"x1":5},"y":{"yt":10,"zt":2,"qa":0,"ft":0}},
{"x":{"x1":3},"y":{"yt":2,"zt":6,"qa":3,"ft":0}}
];
const result1 = getAvg(data1);
console.log('2 objects in the array: ', result1);
const result2 = getAvg(data2);
console.log('3 objects in the array: ', result2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>