Calculate sum properties of child objects
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

Some context
I'm creating a 'man of the match' voting app (using angularjs, angularfire, firebase) and need to be able to calculate the sum of votes that each player has received for that round.

Below is an example of how the data is structured. Each player has a unique id, and votes for their top 3 players. Each player receiving a vote is awarded either 3,2 or 1 point(s).

{
"round-01" : {
"42024e96-6c2c-49d6-92f9-b186f095d95b" : {
"12305e96-4683-49d6-92f9-a153g09gdge1" : 3,
"92b8eae0-4633-4157-94cf-177de09af9de" : 2,
"cf867716-8233-408b-9e8c-f3c39c8f05cb" : 1
},
"92b8eae0-4633-4157-94cf-177de09af9de" : {
"42024e96-6c2c-49d6-92f9-b186f095d95b" : 2,
"12305e96-4683-49d6-92f9-a153g09gdge1" : 3,
"cf867716-8233-408b-9e8c-f3c39c8f05cb" : 1
},
"12305e96-4683-49d6-92f9-a153g09gdge1" : {
"92b8eae0-4633-4157-94cf-177de09af9de" : 2,
"42024e96-6c2c-49d6-92f9-b186f095d95b" : 3,
"cf867716-8233-408b-9e8c-f3c39c8f05cb" : 1
},
"cf867716-8233-408b-9e8c-f3c39c8f05cb" : {
"92b8eae0-4633-4157-94cf-177de09af9de" : 3,
"42024e96-6c2c-49d6-92f9-b186f095d95b" : 1,
"12305e96-4683-49d6-92f9-a153g09gdge1" : 2,
}
}
}

What I would like is to return an object like the following:

{
"round-01" : {
"12305e96-4683-49d6-92f9-a153g09gdge1" : 8,
"92b8eae0-4633-4157-94cf-177de09af9de" : 7,
"42024e96-6c2c-49d6-92f9-b186f095d95b" : 6,
"cf867716-8233-408b-9e8c-f3c39c8f05cb" : 3,
}
}

There may be up to 17 different people submitting votes for each round. So something that works efficiently would be ideal.

I'm using AngularFire so if that allows the solution to be more efficient I'm ok with that. Otherwise regular javascript is ok.

I came across this example http://stackoverflow.com/a/27920500 which is essentially does what I want but is written in lodash.

awarded to lukbl

Crowdsource coding tasks.

2 Solutions

Winning solution

var sum = (function() {
  function sumround(res, r) {
    for (var p in r) {
      if (typeof r[p] === "number") {
        res[p] = (res[p] || 0) + r[p];
      } else if (typeof r[p] === 'object') {
        sumround(res, r[p]);
      }
    }
  }
  return function(a) {
    var res = {};
    for (var p in a) {
      res[p] = {};
      sumround(res[p], a[p]);
    }
    return res;
  }
})();

http://jsfiddle.net/za32ne67/


Assuming that allVotes is the { "round-01": { (...) }, "round-02": { (...) } object

var result = Object.keys(allVotes).reduce(function (voteResults, roundName) {
    var round = allVotes[roundName];

    var roundVotes = Object.keys(round).reduce(function (roundVotes, voterId) {
        var votes = round[voterId];

        Object.keys(votes).forEach(function (playerId) {
            if (roundVotes[playerId] === undefined) {
                roundVotes[playerId] = 0;
            }

            roundVotes[playerId] += votes[playerId];
        });

        return roundVotes;
    }, {});

    voteResults[roundName] = roundVotes;

    return voteResults;
}, {});

Note that you can't rely on the order of object properties. Should you wish to sort the results by most votes, you should convert the result to an array of objects, and then sort them.

View Timeline