How can I shorten this code? (loop) - javascript

So, I just want to shorten the code below:
var start=function(){
var bevetel = document.getElementsByClassName("bevetel");
var yearlyincome2010=0;
var yearlyincome2011=0;
var yearlyincome2012=0;
var yearlyincome2013=0;
var yearlyincome2014=0;
var yearlyincome2015=0;
var yearlyincome2016=0;
for(var i=0; i < bevetel.length; i++) {
if (i<3) {
if (bevetel[i].value) {
yearlyincome2010 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2010").innerHTML=yearlyincome2010;
}
else {}
}
else if (i<7){
if (bevetel[i].value) {
yearlyincome2011 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2011").innerHTML=yearlyincome2011;
}
else {}
}
else if (i<11) {
if (bevetel[i].value) {
yearlyincome2012 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2012").innerHTML=yearlyincome2012;
}
else {}
}
else if (i<15) {
if (bevetel[i].value) {
yearlyincome2013 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2013").innerHTML=yearlyincome2013;
}
else {}
}
else if (i<19) {
if (bevetel[i].value) {
yearlyincome2014 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2014").innerHTML=yearlyincome2014;
}
else {}
}
else if (i<23) {
if (bevetel[i].value) {
yearlyincome2015 += parseInt(bevetel[i].value);
document.getElementById("yearlyincome2015").innerHTML=yearlyincome2015;
}
else {}
}
}
};
Theese #yearlyincome201$'s are IDs of headings...
.bevetel is a class for inputs.
This function calculates each FOUR inputs and gives me the answer of them separately.

var YearsAndIncomes = {
yearIncomes = [],
quarters = [],
}
let year = 2010;
let income = 0;
for (let i = 0; i < bevetel.length; i++) {
income += bevetel[i];
if ((i%4) == 3) {
YearsAndIncomes.yearIncomes.push("YearlyIncome" + year);
YearsAndIncomes.income = income;
income = 0;
year += 1;
}
}
for (let i = 0; i < YearsAndIncomes.yearIncomes.length; i++) {
document.getElementById(YearsAndIncomes.yearIncomes[i]).innerHTML = YearsAndIncomes.income[i];
}
This makes it easier and abstracts your work. Basic rules of looping. Don't hard code anything that you are going to have to add more of later (like years). Try and do everything that is repeated once, (i.e. document.getElementById). This is performed multiple times, but only exists in one place, so you always know where to go if it needs to be fixed or changed.
Also, use 'for (let i' instead of 'for (var i'. Var makes a global variable every time, which you don't want on iterators like 'i'. Let makes a variable that disappears once you are out of the closure, i.e. the 'let i' only exists inside that for loop.

Related

Why are my functions executing out of order at the end of this Connect Four game? It works in some browsers

I'm having two timing issues here, both involving the process in this game once the winning move has been made: https://codepen.io/acchang/pen/XWePpWB
Ideally, I should (1) pick the winning space (2) see the winning space filled (3) have the alert proclaim the winner.
What I see and do not like is:
*checkForWinners() runs
winDeclared() runs and the alert "winner" pop up first
Then after the alert is cleared, drawboard() runs, adding the winning piece to the gameboard.
This does not happen as badly in Firefox. The piece is added at the same time the alert pops up.
Then, in winDeclared(), I also change the display in the top right to also indicate the winner. But swapTurns() seems to execute before winDeclared().
Is that because winDeclared() is two functions deep into checkForWinners()? Is there a way to delay it?
Thanks!
let gameboard = [
[1,2,3,4,5,6,7],
[8,9,10,11,12,13,14],
[15,16,17,18,19,20,21],
[22,23,24,25,26,27,28],
[29,30,31,32,33,34,35],
[36,37,38,39,40,41,42]
];
let playerOne
let playerTwo
let indexPick
let availableSpots
let gameType
let playerOneTurn = true
document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + " "
let itsAOnePlayerGame = true
let isThereAWinner = false
let mainDiv = document.createElement("div");
mainDiv.setAttribute('class', 'mainDiv')
document.body.append(mainDiv);
let selectorHolder = document.createElement("div")
selectorHolder.setAttribute('class', 'selectorHolder')
selectorHolder.setAttribute('id', 'selectorHolder')
mainDiv.append(selectorHolder)
let selectorTable = document.createElement("table")
selectorTable.setAttribute('class', 'selectorTable')
selectorTable.setAttribute('id', 'selectorTable')
selectorHolder.append(selectorTable)
function drawSelector() {
let selectorRow = document.createElement("tr")
selectorRow.setAttribute('class', 'selectorRow')
selectorTable.append(selectorRow)
for (i=0; i<7; i++){
let selectorCell = document.createElement("td")
selectorCell.setAttribute('class', 'selectorCell')
let innerSelectorCell = document.createElement("div")
innerSelectorCell.setAttribute('class', 'innerSelectorCell')
innerSelectorCell.setAttribute('id', [i])
selectorCell.append(innerSelectorCell)
innerSelectorCell.addEventListener("mouseover", function(event) {
if (playerOneTurn == true) {
innerSelectorCell.classList.add('yellowBG')}
else {innerSelectorCell.classList.add('redBG')
}
})
innerSelectorCell.addEventListener("mouseout", function(event) {
if (playerOneTurn == true) {
innerSelectorCell.classList.remove('yellowBG')}
else {innerSelectorCell.classList.remove('redBG')
}
})
innerSelectorCell.onclick = function(){
if (isThereAWinner == true){return}
else {
indexPick = parseInt(this.id)
console.log(indexPick)
claimSpot()
}
}
selectorRow.append(selectorCell)
}
};
drawSelector()
// Draw Main Gameboard
let mainTable = document.createElement("table");
mainTable.setAttribute('class', 'mainTable')
mainDiv.append(mainTable)
function drawBoard() {
for (i=0; i<gameboard.length; i++){
let row = document.createElement("tr")
mainTable.append(row)
for (j=0; j<gameboard[i].length; j++){
let outerCell = document.createElement('td')
outerCell.setAttribute('class', 'outerCell')
row.append(outerCell)
let innerCell = document.createElement('div')
innerCell.setAttribute('class', 'innerCell')
innerCell.classList.add(gameboard[i][j])
innerCell.setAttribute('innerHTML', gameboard[i][j])
outerCell.append(innerCell)
}
}
};
drawBoard()
function validateRadio() {
let ele = document.getElementsByName('gameType');
for(i = 0; i < ele.length; i++) {
if(ele[i].checked){
gameType = (ele[i].value)
beginGame()
}
}
};
function beginGame() {
if (gameType == "1PEasy"){
itsAOnePlayerGame = true
resetBoard()
onePlayerPickSides()
play1PGame()
}
else if (gameType == "1PHard"){
itsAOnePlayerGame = true
resetBoard()
onePlayerPickSides()
play1PGame()
}
else if (gameType == "2P"){
itsAOnePlayerGame = false
resetBoard()
twoPlayerPickSides()
play2PGame()
}
};
function resetBoard() {
playerOneTurn = true
isThereAWinner = false
gameboard = [
[1,2,3,4,5,6,7],
[8,9,10,11,12,13,14],
[15,16,17,18,19,20,21],
[22,23,24,25,26,27,28],
[29,30,31,32,33,34,35],
[36,37,38,39,40,41,42]
];
}
function swapTurns() {
selectorTable.innerHTML = ""
drawSelector()
playerOneTurn = !playerOneTurn
document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + " "
};
// GAMEPLAY
function playerSelects2P() {
findAvailableSpots()
// put an eventListener here?
columnPick = prompt(whosPlaying() + ', choose which column 1-7')
if (availableSpots.includes(parseInt(columnPick)))
{console.log(columnPick)}
else {
alert("not available")
playerSelects2P()}
};
function playerSelects1P() {
if (whosPlaying() == playerTwo) {
findAvailableSpots()
columnPick = availableSpots[Math.floor(Math.random() * availableSpots.length)]
return
}
else {playerSelects2P()}
};
function whosPlaying() {
if (playerOneTurn) {
return "Yellow"
} else {
return "Red"
}
};
// starts from the bottom row and claims spot when there it is a number (unoccupied)
function claimSpot(){
findAvailableSpots()
if (availableSpots.includes(indexPick+1)) {
let i;
for (i = 5; i > -1; i--)
{if (Number.isInteger(gameboard[i][indexPick])) {
gameboard[i].splice((indexPick), 1, whosPlaying())
mainTable.innerHTML = ""
drawBoard()
checkForWinners()
// do I need to put some sort of delay here for it not to go to swap turns right away?
swapTurns()
return
}
}
}
else {
console.log(availableSpots)
alert("Forbidden")
}
};
// if there is a string in row[0], that column is no longer available.
// the cells are numbered from 1 to 7, not per index so you need to add one to indexPick to identify
function findAvailableSpots() {
availableSpots = gameboard[0].filter(x => Number.isInteger(x) == true)
};
function checkForWinners() {
horizontalCheck()
verticalCheck()
downrightCheck()
uprightCheck()
}
// WIN CHECKERS
// a forloop evaluates a section of the matrix, moving through it and seeing if the 3 ahead match.
// it stops before going out of bounds
function findFour(w,x,y,z) {
// Checks first cell against current player and all cells match that player
return ((w == whosPlaying()) && (w === x) && (w === y) && (w === z));
};
function winDeclared() {
isThereAWinner = true
alert("winner")
document.getElementsByName("announcements")[0].innerHTML = whosPlaying() + " wins! "
// this does not show, it snaps to swap places
};
function uprightCheck() {
for (r=5; r>2; r--) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r-1][c+1], gameboard[r-2][c+2], gameboard[r-3][c+3])) {
winDeclared()
return
}
}
}
};
function downrightCheck() {
for (r=0; r<3; r++) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r+1][c+1], gameboard[r+2][c+2], gameboard[r+3][c+3])) {
winDeclared()
return
}
}
}
};
function verticalCheck() {
for (r=5; r>2; r--) {
for (c=0; c<7; c++){
if (findFour(gameboard[r][c], gameboard[r-1][c], gameboard[r-2][c], gameboard[r-3][c])) {
winDeclared()
return
}
}
}
};
function horizontalCheck() {
for (r=0; r<6; r++) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r][c+1], gameboard[r][c+2], gameboard[r][c+3])) {
winDeclared()
return
}
}
}
};
When you manipulate the DOM, the operation itself is syncrhonous but the browser decides when the user will actually see the changes. Sometimes, the broswer will not have time to redraw before the prompt appears. To get around this, you can wrap the alert in a setTimeout() to delay the alert.
setTimeout(
function() {
alert("winner")
}, 10)

For Loop with Changing Variable Names

I would like to create a for loop that performs the same operation with different variable names. For instance; in the below code, I would like to loop the code 6 times, and every instance of the word "civil" would be variable based on values in an array.
civilSheet.activate();
//grab values from column A and determine which rows the Hot List, Coordination Items, and Responsibilities information
var civilData = civilSheet.getRange('A:A').getValues();
for(var j=0;j<civilData.length;j++){
if(civilData[j] == "~Hot List~"){
var civilHot = j+3;
} else if(civilData[j] == "~Coordination Items~"){
var civilCoord = j+1;
} else if(civilData[j] == "~Responsibilities~"){
var civilResp = j+1;
}
}
The goal is something along the lines of the below code:
var variableNames = ["civil", "struct", "avl", "fpm", "electrical", "arch"];
for(var i=0;i<variableNames.length;i++){
var i+"Data" = i+"Sheet".getRange('A:A').getValues();
for(var j=0;j<i+"Data".length;j++){
if(i+"Data"[j] == "~Hot List~"){
var i+"Hot" = j+3;
} else if(i+"Data"[j] == "~Coordination Items~"){
var i+"Coord" = j+1;
} else if(i+"Data"[j] == "~Responsibilities~"){
var i+"Resp" = j+1;
}
}
}
You don't need dynamic variables names. You need variables to dynamically refer to different objects. You can simply add a another for-loop with arrays to reuse the same code.
Assuming variableNames are sheetNames,
const sheetNames = ['civil', 'struct', 'avl', 'fpm', 'electrical', 'arch'];
const ss = SpreadsheetApp.getActive();
for (let si = 0; si < sheetNames.length; si++) {
let thisSheet = ss.getSheetByName(sheetNames[si]);
let thisData = thisSheet.getRange('A:A').getValues();
for (let j = 0; j < thisData.length; j++) {
if (thisData[j][0] == '~Hot List~') {
let thisHot = j + 3;
} else if (thisData[j][0] == '~Coordination Items~') {
let thisCoord = j + 1;
} else if (thisData[j][0] == '~Responsibilities~') {
let thisResp = j + 1;
}
}
}

creating a new array out of an object

I've created a JavaScript object to get the number of times a character repeats in a string:
function getFrequency(string) {
// var newValsArray =[];
var freq = {};
for (var i=0; i<string.length;i++) {
var character = string.charAt(i);
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
}
Now, I'm trying to construct a new string composed of the keys & their properties (the letters) & numbers of times the letters repeat if the number (property) is more than one but I keep getting undefined and I don't know why:
function newString(freq){
var newValsArray = [];
for (var prop in freq) {
if (freq[prop]>1){
newValsArray.push(prop + freq[prop]);
}
else if (freq[prop] < 2){
newValsArray.push(prop);
}
}
return newValsArray;
}
I feel like my syntax is off or something... if anyone has any suggestions, I'd really appreciate it...
You aren't explicitly returning anything from newString(), so it will return undefined. It sounds like you want something like this:
return newValsArray.join('');
at the end of newString() to construct an actual string (instead of returning an array). With that change, newString(getFrequency("Hello there") will return 'He3l2o thr'.
function getFrequency(string) {
// var newValsArray =[];
var freq = {};
for (var i = 0; i < string.length; i++) {
var character = string.charAt(i);
if (freq[character]) {
freq[character] ++;
} else {
freq[character] = 1;
}
}
return freq;
}
function newString(freq) {
var newValsArray = [];
for (var prop in freq) {
if (freq[prop] > 1) {
newValsArray.push(prop + freq[prop]);
} else if (freq[prop] < 2) {
newValsArray.push(prop);
}
}
return newValsArray.join("");
}
var mystring = "Here are some letters to see if we have any freq matches and so on.";
var results = newString(getFrequency(mystring));
var elem = document.getElementById("results");
elem.innerHTML = results;
<div id="results"></div>
You are not returning anything from the newString function. Add return newString; as the last line of the newString function. Adding that line does result in something being returned, though I can't tell if it is what you expected.
var text = "asdfjhwqe fj asdj qwe hlsad f jasdfj asdf alhwe sajfdhsadfjhwejr";
var myFreq = getFrequency(text);
show(myFreq);
var myNewValsArray = newString(myFreq);
show(myNewValsArray);
function getFrequency(string) {
// var newValsArray =[];
var freq = {};
for (var i=0; i<string.length;i++) {
var character = string.charAt(i);
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
}
function newString(freq){
var newValsArray = [];
for (var prop in freq) {
if (freq[prop]>1){
newValsArray.push(prop + freq[prop]);
}
else if (freq[prop] < 2){
newValsArray.push(prop);
}
}
return newValsArray; // ******** ADD THIS LINE
}
function show(msg) {
document.write("<pre>" + JSON.stringify(msg, null, 2) + "</pre>");
}

Javascript: scope effect despite order of execution

Please note: This is not a question about scope, per se. I understand that in order to make the code work, I should make a deep copy of the variable board rather than assigning var tboard = board. However, I am not clear why making a shallow copy has the effect I describe below.
I am experiencing something I find baffling. Basically, a global variable (board) gets altered and I have no clue how. board is initialized in the function NewGame() (which is called from select()) as an empty array. After it is initialized, nothing else is called until the user clicks a square on the board (assuming the user has selected Xs for simplicity). When that happens, the function playerMove() is called. The baffling thing is that console.log(board) at the top of playerMove() prints out an array that has an x is the clicked position and os everywhere else (ie not empty). This is bizarre because the board is empty at the end of select() (which called NewGame()) and nothing else should happen in between. To demonstrate this, I print out the function name at the top of each function and I print out the board variable in the select() function and playerMove() function to show that it changes despite nothing else being called. Please note that to get this behavior, refresh the page (otherwise the board variable starts out full of os). I think this must be somewhat an issue of scope (because I am not making a deep copy of board) but it's strange because I have no clue what is being called that is changing the variable before it gets printed out at the top of playerMove().
Here is the link to my pen and the code: http://codepen.io/joshlevy89/pen/MKjxop?editors=101
$(document).ready(function() {
var pSym; // player's symbol
var cSym; // computer's symbol
var board;
var whosMove; // can be "player" or "computer" or "neither"
var gameOver;
setup();
$("#newgame").on('click', '#X', select);
$("#newgame").on('click', '#O', select);
$("#restart").on('click', setup);
$("table").on('click', 'td', playerMove);
function playerMove()
{
console.log('playerMove');
console.log(board);
if (whosMove === "player")
{
var val = $(this).data('value');
$('#g' + val).text(pSym);
var arr = PositionToCoords(val);
board[arr[0]][arr[1]] = pSym;
var tboard = board;
var gc = gameCheck(tboard);
if (gc>=0)
{
endGame(gc);
setTimeout(function(){setup();}, 1000);
return;
}
whosMove = "computer";
computerMove();
}
}
function computerMove() {
console.log('computerMove');
//var p1 = Math.floor(Math.random() * 3);
//var p2 = Math.floor(Math.random() * 3);
var tboard = board;
var pos = chooseMove(tboard);
var arr = PositionToCoords(pos);
board[arr[0]][arr[1]] = cSym;
DrawPosition(arr[0], arr[1], cSym);
var tboard = board;
var gc = gameCheck(tboard);
if (gc>=0) {
endGame(gc);
setTimeout(function(){setup();}, 1000);
return;
}
whosMove = "player";
}
function chooseMove(inboard) {
console.log('chooseMove');
// get the possible moves
var moves=[];
var scores = [];
for (var i=1;i<10;i++) {
var arr = PositionToCoords(i);
if (inboard[arr[0]][arr[1]] === undefined) {
moves.push(i);
var tboard = inboard;
tboard[arr[0]][arr[1]] = cSym;
var gc = gameCheck(tboard);
scores.push(gc);
}
}
//console.log(moves);
//console.log(scores);
return moves[0]; // TEMPORARY
}
function endGame(gc) {
console.log('endGame');
var str;
if (gc===1) { // somebody won
if (whosMove==="player"){
str = "You Won!"
}
else {
str = "You Lost :(";
}
}
else if (gc === 0){//draw
str = "It's a draw."
}
html = '<div id="closer">' + str + '</div>';
$('#endgame').html(html);
}
function gameCheck(tboard) {
console.log('gameCheck');
// get symbol to check for
var sym;
if (whosMove === "player") {
sym = pSym;
} else {
sym = cSym;
}
// check if in a row
var hrow;
var vrow;
// check for horizonal row
for (var i = 0; i < 3; i++) {
hrow = true;
vrow = true;
for (var j = 0; j < 3; j++) {
if (tboard[i][j] !== sym) {
hrow = false;
}
if (tboard[j][i] !== sym) {
vrow = false;
}
}
if ((hrow) || (vrow)) {
return 1;
}
}
var fdrow = true;
var bdrow = true;
for (var i = 0; i < 3; i++) {
if (tboard[i][i] !== sym) {
fdrow = false;
}
if (tboard[i][2 - i] !== sym) {
bdrow = false;
}
}
if ((fdrow) || (bdrow)) {
return 1;
}
// otherwise, check if board is full
var full = true;
for (var i = 1; i < 10; i++) {
var arr = PositionToCoords(i);
if (tboard[arr[0]][arr[1]] === undefined) {
full = false;
break;
}
}
if (full === true) {
return 0;
}
// if neither 0 (tie) or win (1), return -1 (game not over)
return -1;
}
function select() {
console.log('select');
pSym = $(this).data('value');
$('#newgame').html('');
NewGame();
console.log(board);
}
function setup() {
console.log('select');
$('#endgame').html('');
html = '<div id="opener">Xs or Os? <div id="buttons">';
html += '<div id="X" data-value="X" class="btn btn-default">Xs</div>';
html += '<div id="O" data-value="O" class="btn btn-default">Os</div>';
html += '</div></div>';
$('#newgame').html(html);
}
function NewGame() {
console.log('NewGame');
$('td').empty();
board = new Array(3);
for (i = 0; i < 3; i++) {
board[i] = new Array(3)
};
if (pSym === "X") {
cSym = "O";
whosMove = "player";
} else {
cSym = "X";
whosMove = "computer";
computerMove();
}
}
function DrawPosition(p1, p2, sym) {
console.log('DrawPosition');
var pos = p1 * 3 + (p2 + 1);
$("#g" + pos).text(sym)
}
function PositionToCoords(pos) {
console.log('PositionToCoords');
var p1 = Math.ceil(pos / 3) - 1;
var p2 = ((pos - 1) % 3);
var arr = [p1, p2];
return arr;
}
});
Thanks in advance.
Simply add the break in the for loop fixes the problem. Am I missing anything?
function chooseMove(inboard) {
console.log('chooseMove');
// get the possible moves
var moves = [];
var scores = [];
for (var i = 1; i < 10; i++) {
var arr = PositionToCoords(i);
if (inboard[arr[0]][arr[1]] === undefined) {
moves.push(i);
var tboard = inboard;
tboard[arr[0]][arr[1]] = cSym;
var gc = gameCheck(tboard);
scores.push(gc);
break; // <<<<<<<<<<<< This break guarantees that the computer only makes one move
}
}
//console.log(moves);
//console.log(scores);
return moves[0]; // TEMPORARY
}

How to create a function in Javascript which calls other functions

I have a task that I am trying to solve. I have created a function named printRange which will print the number range between rangeStart and rangeStop and all values separated by a comma.
var rangeStart, rangeStop;
function printRange(rangeStart, rangeStop) {
var numberRow = "";
for(var i=rangeStart; i <= rangeStop; i++) {
numberRow += "," + i;
}
return numberRow;
}
I have also created another function named "printRangeReversed" which will print the range but in the opposite order. Again, the values will be separated by a comma.
function printRangeReversed(rangeStart, rangeStop) {
var numberRow = "";
for(var i=rangeStop; i >= rangeStart; i--) {
numberRow += "," + i;
}
return numberRow;
}
Now I should create a new function called printAnyRange. If 'rangeStart' is smaller than 'rangeStop' then I should call the function 'printRange()'. If 'rangeStart' is greater than 'rangeStop' then I should call the function 'printRangeReversed()'.
How do I do this? I have tried myself with the code below but does not get any satisfying results... Thanks in advance!
function printAnyRange(rangeStart, rangeStop) {
var numberRow = "";
if(rangeStart < rangeStop) {
printRange();
}
else {
printRangeReversed();
}
}
printAnyRange(24, 41);
You forgot to supply the arguments to printRange() and printRangeReversed().
Just change
if(rangeStart < rangeStop) {
printRange();
}
else {
printRangeReversed();
}
with
if(rangeStart < rangeStop) {
printRange(rangeStart, rangeStop);
}
else {
printRangeReversed(rangeStart, rangeStop);
}
and it should work.

Categories

Resources