Murakami
Murakami

Reputation: 3760

Counting inside the array of arrays in JavaScript

I'm trying to solve this issue:

I have an array like that (I cannot change the way it is structured):

[
[{rivals: ['player1','player2'], winner: "player1"}],
[{rivals: ['player1','player3'], winner: "none"}],
[{rivals: ['player2','player3'], winner: "player3"}],
[{rivals: ['player1','player4'], winner: "none"}]
]

What I'm trying to get is: count every player's points, ie if a player (f.ex player1) wins the player counter is increasing by 3points, if none of rivals win then both counters will be increased by 1point. So at the end I would like to get something like (basing on the example above):

const player1Counter = 5;
const player2Counter = 0;
const player3Counter = 4;
const player4Counter = 1;

Thank you for helping!

Upvotes: 1

Views: 136

Answers (2)

ibrahim mahrir
ibrahim mahrir

Reputation: 31682

You can use Array.reduce() for that. For each sub array, loop the rivals array and check if a player already has a counter or not. If not, create one for that player with a score of 0. Then simply check the winner property to update the records. If there is a winner, increase that player's score by 3, if not, increment both players' counters by 1:

var result = arr.reduce(function(result, sub) {
    var obj = sub[0];
    obj.rivals.forEach(function(rival) {
        result[rival] = result[rival] || 0;
    });
    if(obj.winner === "none") {
        obj.rivals.forEach(function(rival) {
            result[rival]++;
        });
    } else {
        result[obj.winner] += 3;
    }
    return result;
}, {});

This can be refactored to use ES6 arrow functions like so:

let result = arr.reduce((result, {0: obj}) => {
    obj.rivals.forEach(rival => result[rival] = result[rival] || 0);
    if(obj.winner === "none") {
        obj.rivals.forEach(rival => result[rival]++);
    } else {
        result[obj.winner] += 3;
    }
    return result;
}, {});

Example:

let arr = [[{rivals: ['player1','player2'], winner: "player1"}],[{rivals: ['player1','player3'], winner: "none"}],[{rivals: ['player2','player3'], winner: "player3"}],[{rivals: ['player1','player4'], winner: "none"}]];

let result = arr.reduce((result, {0: obj}) => {
    obj.rivals.forEach(rival => result[rival] = result[rival] || 0);
    if(obj.winner === "none") {
        obj.rivals.forEach(rival => result[rival]++);
    } else {
        result[obj.winner] += 3;
    }
    return result;
}, {});

console.log(result);

Note: This solution doesn't produce separate variables for the counters. It instead generates an object that holds the scores which is tidier and cleaner.

Upvotes: 6

samanime
samanime

Reputation: 26537

Here is a concise method that can add them up.

const results = [
  [{ rivals: ['player1','player2'], winner: "player1" }],
  [{ rivals: ['player1','player3'], winner: "none" }],
  [{ rivals: ['player2','player3'], winner: "player3" }],
  [{ rivals: ['player1','player4'], winner: "none" }]
];

const scores = results.reduce((scores, [{ rivals, winner }]) => 
  Object.assign(scores, winner == 'none'
    ? { 
      [rivals[0]]: (scores[rivals[0]] || 0) + 1,
      [rivals[1]]: (scores[rivals[1]] || 0) + 1
    } : { [winner]: (scores[winner] || 0) + 3 }),
  {});

console.log(scores);

If you want to make sure they have 0, you can tweak it a little bit:

const results = [
  [{ rivals: ['player1','player2'], winner: "player1" }],
  [{ rivals: ['player1','player3'], winner: "none" }],
  [{ rivals: ['player2','player3'], winner: "player3" }],
  [{ rivals: ['player1','player4'], winner: "none" }]
];

const scores = results.reduce((scores, [{ rivals, winner }]) => 
  Object.assign(scores, winner == 'none'
    ? { 
      [rivals[0]]: (scores[rivals[0]] || 0) + 1,
      [rivals[1]]: (scores[rivals[1]] || 0) + 1
    } : { 
      [rivals[0]]: (scores[rivals[0]] || 0),
      [rivals[1]]: (scores[rivals[1]] || 0),
      [winner]: (scores[winner] || 0) + 3 
    }),
  {});

console.log(scores);

Upvotes: 1

Related Questions