0

I have a JSON object returned from a web service, which is an array of objects. I need to add the "data" arrays together to form a summed array. The JSON response looks like this:

[  
  {  
    "data":[  
       0,3,8,2,5
    ],
    "someKey":"someValue"
  },
  {  
    "data":[  
       3,13,1,0,5
    ],
    "someKey":"someOtherValue"
  }
]

There could be N amount of objects in the array. The desired output for the above example would be:

[3, 16, 9, 2, 10]

I was intending on creating an empty array variable (var arr), then looping over the objects, and for each object, loop through the "data" key and for each key increment the corresponding key in arr by the value.

Is there a more efficient way of doing this using some sort of merge function?

2
  • Does data array have always the same length ? Commented Oct 22, 2015 at 18:21
  • yes for every object it will be the same length Commented Oct 22, 2015 at 18:22

3 Answers 3

2

How about this, I believe it should work for all cases.

var data = [{
  "data": [
    0, 3, 8, 2, 5
  ],
  "someKey": "someValue"
}, {
  "data": [
    3, 13, 1, 0, 5
  ],
  "someKey": "someOtherValue"
}];

var datas = data.reduce(function(a, b) {
  b.data.forEach(function(x, i) {
    a[i] = a[i] || 0;
    a[i] += x;
  });
  return a;
}, []);

console.log(datas);

Sign up to request clarification or add additional context in comments.

1 Comment

this also seems to work. I wonder if this is more/less efficient than the accepted answer? my guess is the difference will be neglible
1

If every object has the same data length, you can try with:

var input; // Your input data
var output = [];
for (var i = 0; i < input[0].data.length; i++) {
  output[i] = input.reduce(function(prev, item) {
    return +(item.data[i]) + prev;
  }, 0);
}

console.log(output);
// [3, 16, 9, 2, 10]

If every object has different data size:

var input; // Your input data
var i = 0, output = [];
while (true) {
  var outOfIndex = true;

  var sum = input.reduce(function(prev, item) {
    if (item.data[i] !== undefined) {
      outOfIndex = false;
    }
    return +(item.data[i]) + prev;
  }, 0);

  if (outOfIndex) {
    break;
  }
  output[i++] = sum;
}

console.log(output);
// [3, 16, 9, 2, 10]

4 Comments

ah sorry I misunderstood your question in the comments. "data" won't always be 5, it can be any length, but it will always be the same length for each object (if that's clear :) )
@ExoticChimp Fixed for any length.
Changing the hardcoded "5" with "input[0].data.length" should do the trick (assuming that there will always be 1+ entries in each data set... if empty data sets are possible, check against that first). That said, I suggest you implement the straight forward solution you proposed on your question and contrast it with this suggestion; unless there's a significant difference in execution time, I would favor using a loop, as it will probably be easier to maintain and understand at a glance.
I think, for me personally, I like the first solution with a check against an empty dataset and it's sufficiently readable. Though you're right, I should benchmark them and see
0

Slightly less imperative solution:

//zip takes two arrays and combines them per the fn argument
function zip(left, right, fn) {
   var shorter = (right.length > left.length) ? left : right;
   return shorter.map(function(value, i) {
       return fn(left[i], right[i]);
   });
}

//assuming arr is your array of objects. Because were using
//zip, map, and reduce, it doesn't matter if the length of the
//data array changes
var sums = arr
    .map(function(obj) { return obj.data; })
    .reduce(function(accum, array) {

        //here we want to combine the running totals w/the current data
        return zip(accum, array, function(l, r) { return l + r; });
    });

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.