0

With an associative array of 12 elements;

 this.rolls = {1:[], 2:[], 3:[], 4:[], 5:[],
                6:[], 7:[], 8:[], 9:[],10:[], 11:[], 12:[]};

what is the most effective way to get the sum of just the first 10 elements. The code below currently sums all elements;

  var sum = 0;
  for (var k in this.rolls) {
    vals = this.rolls[k];
    for (var i=0; i<vals.length; i++) {
      sum += vals[i] || 0
    };
  };
  this.score = sum

The first 10 elements being:

 this.rolls = {1:[], 2:[], 3:[], 4:[], 5:[],
                    6:[], 7:[], 8:[], 9:[],10:[]};

Here is the full code:

function Game() {
  this.score = 0;
  this.frameOver = false;
  this.rolls = {1:[], 2:[], 3:[], 4:[], 5:[],
                6:[], 7:[], 8:[], 9:[],10:[], 11:[], 12:[]};
  this.currentFrame = 1;
  // this.lastFrameStrike = false;
  // this.lastFrameSpare = false;
  // this.isStrike = false
};

Game.prototype.roll = function(pins) {
  this.strikeOrSpare(pins);
  this.bonusDistributor(pins);
  this.rolls[this.currentFrame].push(pins);
  this.scoreUpdater(pins);
  this.frameHandler(pins);
  this.nextFrameBonus(pins)
};

// --------------------------------------------------

Game.prototype.strikeOrSpare = function(pins) {
  if (pins === 10) {
    this.isStrike = true;
    this.frameOver = true
  }
  else if (this.rolls[this.currentFrame][0] + pins === 10) {
    this.isSpare = true;
    this.frameOver = true
  };
};

// --------------------------------------------------

Game.prototype.bonusDistributor = function(pins) {
  if(this.wasSpare) { this.addToLastSpare(pins) };
  if(this.wasStrike) { this.addToLast(pins) };
  if(this.wasStrike2 && this.currentFrame > 1) { this.addToLastAgain(pins) };
};

// --------------------------------------------------

Game.prototype.addToLast = function(pins) {
  this.rolls[this.currentFrame - 1][0] += pins
};

Game.prototype.addToLastAgain = function(pins) {
  this.rolls[this.currentFrame - 2][0] += pins
};

Game.prototype.addToLastSpare = function(pins) {
  this.rolls[this.currentFrame - 1][1] += pins;
  this.wasSpare = false
};

// --------------------------------------------------

Game.prototype.scoreUpdater = function(pins) {
  var sum = Object.keys(this.rolls).sort(function (a, b) {
      return (+a) - (+b);
  }).slice(0, 10).reduce(function (p, c) {
      return p + this.rolls[c].reduce(function (p, c) {
          return p + c;
      }, 0);
  }, 0);
};

Game.prototype.frameHandler = function(pins) {
  if (this.frameOver) {
    this.currentFrame++; this.frameOver = !this.frameOver
  } else {
  this.frameOver = !this.frameOver;
  };
};

Game.prototype.nextFrameBonus = function(pins) {
  if (this.isSpare) {
    this.wasSpare = true;
    this.isSpare = false
    if (this.wasStrike) {
      this.wasStrike = false;
      this.wasStrike2 = true
    }
  } else if (this.isStrike && this.wasStrike) {
    this.wasStrike2 = true
  } else if (this.isStrike) {
    this.isStrike = false;
    this.wasStrike = true
  } else if (this.wasStrike) {
    this.wasStrike = false;
    this.wasStrike2 = true
  } else if (this.wasStrike2) {
    this.wasStrike2 = false
  };
};

// --------------------------------------------------
Barris
  • 969
  • 13
  • 29

3 Answers3

3

The first issue you'll have problems with is that this.rolls is an Object, not an Array, so for (var k in this.rolls) is not guaranteed to enumerate the keys in order. So the first problem to solve is to take those first 10 keys, convert them to numbers, and sort them. Here I'm using all native Array and Object methods:

var rolls = this.rolls;
var sum = Object
  // Get all keys
  .keys(rolls)
  // Convert string keys to integers
  .map(function (key) { return parseInt(key, 10); })
  // Sort in ascending order
  .sort()
  // Take the first 10
  .slice(0, 10)
  // Get the arrays for each key
  .map(function (key) { return rolls[key]; })
  // Merge all arrays into one array
  .reduce(function (allNumbers, array) { return allNumbers.concat(array); }, [])
  // Sum all numbers
  .reduce(function (sum, number) { return sum + (number || 0); }, 0);
Jacob
  • 77,566
  • 24
  • 149
  • 228
1

Sort and filter object keys, then use them to reduce arrays to sums

var rolls = this.rolls;
var sum = Object.keys(rolls).sort(function (a, b) {
    return (+a) - (+b);
}).slice(0, 10).reduce(function (p, c) {
    return p + rolls[c].reduce(function (p, c) {
        return p + c;
    }, 0);
}, 0);

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
0

EDITED: There is one mistake in your example. You cannot use this.rolls inside of forEach. Declare it (var self = this; or my version is var rolls = this.rolls; outside)

I suggest following code for calculating sum:

var sum = 0;
var rolls = this.rolls;
Object.keys(rolls).forEach(function (key, i, array) {
    if (i < 10) {
        var item = rolls[key].slice();
        while (item.length) {
            sum += item ? Number(item.shift()) : 0 + item ? Number(item.pop()) : 0;
        }
    }
});
console.log(sum, this.rolls);
this.score = sum;

There is your full example:

function Game() {
    this.score = 0;
    this.frameOver = false;
    this.rolls = {1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: []};
    this.currentFrame = 1;
    // this.lastFrameStrike = false;
    // this.lastFrameSpare = false;
    // this.isStrike = false
}

Game.prototype.roll = function (pins) {
    this.strikeOrSpare(pins);
    this.bonusDistributor(pins);
    this.rolls[this.currentFrame].push(pins);
    this.scoreUpdater(pins);
    this.frameHandler(pins);
    this.nextFrameBonus(pins);
};

// --------------------------------------------------

Game.prototype.strikeOrSpare = function (pins) {
    if (pins === 10) {
        this.isStrike = true;
        this.frameOver = true;
    }
    else if (this.rolls[this.currentFrame][0] + pins === 10) {
        this.isSpare = true;
        this.frameOver = true;
    }
};

// --------------------------------------------------

Game.prototype.bonusDistributor = function (pins) {
    if (this.wasSpare) {
        this.addToLastSpare(pins);
    }
    if (this.wasStrike) {
        this.addToLast(pins);
    }
    if (this.wasStrike2 && this.currentFrame > 1) {
        this.addToLastAgain(pins);
    }
};

// --------------------------------------------------

Game.prototype.addToLast = function (pins) {
    this.rolls[this.currentFrame - 1][0] += pins;
};

Game.prototype.addToLastAgain = function (pins) {
    this.rolls[this.currentFrame - 2][0] += pins;
};

Game.prototype.addToLastSpare = function (pins) {
    this.rolls[this.currentFrame - 1][1] += pins;
    this.wasSpare = false;
};

// --------------------------------------------------

Game.prototype.scoreUpdater = function (pins) {
    var sum = 0;
//    var self = this;
    var rolls = this.rolls;
    Object.keys(rolls).forEach(function (key, i, array) {
        if (i < 10) {
            var item = rolls[key].slice();
            while (item.length) {
                sum += item ? Number(item.shift()) : 0 + item ? Number(item.pop()) : 0;
            }
        }
    });
    console.log(sum, this.rolls);
    this.score = sum;
};

Game.prototype.frameHandler = function (pins) {
    if (this.frameOver) {
        this.currentFrame++;
        this.frameOver = !this.frameOver;
    } else {
        this.frameOver = !this.frameOver;
    }
};

Game.prototype.nextFrameBonus = function (pins) {
    if (this.isSpare) {
        this.wasSpare = true;
        this.isSpare = false;
        if (this.wasStrike) {
            this.wasStrike = false;
            this.wasStrike2 = true;
        }
    } else if (this.isStrike && this.wasStrike) {
        this.wasStrike2 = true;
    } else if (this.isStrike) {
        this.isStrike = false;
        this.wasStrike = true;
    } else if (this.wasStrike) {
        this.wasStrike = false;
        this.wasStrike2 = true;
    } else if (this.wasStrike2) {
        this.wasStrike2 = false;
    }
};
var game = new Game();
game.scoreUpdater();
Sherali Turdiyev
  • 1,745
  • 16
  • 29