Access dynamic variable names in for loop [duplicate] - javascript

This question already has answers here:
"Variable" variables in JavaScript
(9 answers)
Closed 4 years ago.
I have a basic problem with for loop "tostring" iteration.
Description:
There are 12 <input type="text"> containers.
I want to detect when user writes si word in any 3 out of 12 containers.
If user types si exactly 3 times I set an alert called You made it!.
Problem:
Most likely that line is incorrect: if(cube[i].includes("si")).
Not sure how to check all containers in for loop just to count the number of si word. (adding number to string in for loop to call another variables e.g. cube1, cube2, cube3 etc.)
Any help would be appreciated, thank you :)
function transmute() {
var cube1 = document.getElementById("cube_slot1").value;
var cube2 = document.getElementById("cube_slot2").value;
var cube3 = document.getElementById("cube_slot3").value;
var cube4 = document.getElementById("cube_slot4").value;
var cube5 = document.getElementById("cube_slot5").value;
var cube6 = document.getElementById("cube_slot6").value;
var cube7 = document.getElementById("cube_slot7").value;
var cube8 = document.getElementById("cube_slot8").value;
var cube9 = document.getElementById("cube_slot9").value;
var cube10 = document.getElementById("cube_slot10").value;
var cube11 = document.getElementById("cube_slot11").value;
var cube12 = document.getElementById("cube_slot12").value;
var counter = 0;
for (var i = 1; i <= 12; i++) {
if (cube[i].includes("si")) {
counter += 1;
}
}
if (counter == 3) {
alert("You made it!");
}
}

Perhaps you could simplify your approach, but iterating over your elements within a for loop (via a dynamic id), and counting occasions where the si substring is found in input values.
If three or more cases are encountered, display the alert() and then break early from the loop:
function transmute() {
for(var i = 1; i <= 12; i++) {
var id = 'cube_slot' + i;
var value = document.getElementById(id).value;
if(value.includes('si')) {
counter += 1;
}
if(counter >= 3) {
alert("You made it!");
break
}
}
}

Related

Google Script working as 2 separate scripts but not inside the same function

Basically I have a script that is in 4 blocks:
1. Copies within a range each row provided it meets a criteria
2. Removes all empty rows
3. Sets all numbers as percentage
4. Applies conditional cell formatting to one of the columns
The 4th part is the one that is causing me issues. The script runs without any error message AND block 4 works perfectly fine if it's in another script alone with the same variables defined but as soon as it is inside the same function as the others it simply doesn't run without any error message of any kind.
Tried changing the name of the variables to single use ones to ensure it wasn't because one of the "var" was modified above it, removing the "else if" to keep only an "if" in the loop, moving it around to other parts of the script but if the block 1 is in the script then block 4 won't apply (will apply if it is only with 2 & 3.
2 & 3 which follow the same structure work well with 1.
Does any one have any clue what's wrong with my script ? :)
Each block is commented with what it does
function copy() {
//Set variables & criterion to choose which rows to copy
var s = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1bEiLWsbFszcsz0tlQudMBgTk5uviyv_wDx7fFa8txFM/edit');
var ssSource = s.getSheetByName('Variations');
var ssDest = s.getSheetByName('Email');
var lastRowSource = ssSource.getLastRow();
var lastRowDest = ssDest.getLastRow();
var lastColSource = ssSource.getLastColumn()
var criteria = 0;
var titles = ssSource.getRange(1,1,1, lastColSource).getValues()
//Copies the range
ssDest.getRange(1,1,1, lastColSource).setValues(titles)
for (var i = 2; i < lastRowSource; i++ ) {
var test = ssSource.getRange(i ,1);
Logger.log(test.getValue()+ ' <? ' + criteria);
if (ssSource.getRange(i ,6).getValue() > criteria) {
ssSource.getRange(i ,1,1,ssSource.getLastColumn()).copyTo(ssDest.getRange(i ,1,1,ssSource.getLastColumn()), {contentsOnly:true}); // copy/paste content only
}
}
//Removes empty rows
var data = ssDest.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n].join().replace(/,/g,'')!=''){ targetData.push(data[n])};
Logger.log(data[n].join().replace(/,/g,''))
}
ssDest.getDataRange().clear();
ssDest.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
//Formats numbers as percentages
var rangePercent = ssDest.getRange(1,1,ssDest.getLastRow(),ssDest.getLastColumn());
var rowsPercent = rangePercent.getNumRows();
var colsPercent = rangePercent.getNumColumns();
for(var rowPercent = 1; rowPercent <= rowsPercent; rowPercent++) {
for(var colPercent = 1; colPercent <= colsPercent; colPercent++) {
var cellPercent = rangePercent.getCell(rowPercent, colPercent);
var valuePercent = cellPercent.getValue();
if(typeof(valuePercent) == 'number') {
cellPercent.setNumberFormat("##.#%");
}
}
}
//Adds conditional background colours
for (var z = 2; z < lastRowDest+1;z++) {
var avgCpc = 4;
var rangeColour = ssDest.getRange(z,avgCpc);
var dataColour = rangeColour.getValue()
if (dataColour < 0) {
ssDest.getRange(z,avgCpc).setBackground('#d9ead3')
}
else if (dataColour > 0) {
ssDest.getRange(z,avgCpc).setBackground('#f4cccc')
}
}
//Centers Values
}
The problem you're having is your code has performance issues because you're calling too many times methods such as getRange() and getValue() inside various loops, therefore Apps Script can't keep up with all those calls. Please check Best Practices.
Having said that, I modified your code in order to make it more efficient. Besides your copy function, I added two more functions to make the code more readable.
function copy
As before this function sets the variables, but now it calls two other functions, which are setPositiveCostValues and formatCells
function copy() {
//Set variables & criterion to choose which rows to copy
var ss = SpreadsheetApp.openByUrl('your-url');
var ssSource = ss.getSheetByName('Variations');
var ssDest = ss.getSheetByName('Email');
// set the title
var titles = ssSource.getRange(1,1,1, ssSource.getLastColumn()).getValues();
ssDest.getRange(1,1,1, ssSource.getLastColumn()).setValues(titles);
// get the positive values you want from the cost col
var positiveValues = setPositiveCostValues(ssSource, ssDest, ssSource.getLastRow());
// fomrat the cells you want as percentage and set the color
formatCells(ssDest, positiveValues);
}
function setPositiveCostValues
This will take the values where the cost is positive and it will get rip off of the cells with empty values and "n/a" values.
function setPositiveCostValues(ssSource,ssDest, lastRowSource){
var postiveCost = ssSource.getRange(2, 1, lastRowSource, 6).getValues();
// this loop will clean the empty elements and the ones that only have n/a
for (var i = postiveCost.length - 1; i >= 0; i--) {
if (postiveCost[i][0]) {
postiveCost.splice(i + 1, postiveCost.length - (i + 1));
postiveCost = postiveCost.filter(function(el){ return el != 'n/a'})
break;
}
}
return postiveCost;
}
function formatCells
This will format the cells in the cost col as a percentage and will set the right color in your avgCpc col.
function formatCells(ssDest, postiveCost){
var avgCpc = 4, cost = 6, row = 2, criteria = 0;
// iterate over the array and depending on the criteria format the cells
postiveCost.forEach(function(el){
if(el[cost - 1] > criteria){
var ssDestRange = ssDest.getRange(row, 1, 1, cost);
ssDestRange.setValues([el]);
ssDestRange.getCell(1, cost).setNumberFormat("##.#%");
// set the color depending on the avgCpc value condition
if(el[avgCpc - 1] < criteria) ssDest.getRange(row, avgCpc).setBackground('#d9ead3');
else ssDest.getRange(row, avgCpc).setBackground('#f4cccc');
row++;
}
});
}
Code
Your whole code now it will look like this:
function copy() {
//Set variables & criterion to choose which rows to copy
var ss = SpreadsheetApp.openByUrl('your-url');
var ssSource = ss.getSheetByName('Variations');
var ssDest = ss.getSheetByName('Email');
// set the title
var titles = ssSource.getRange(1,1,1, ssSource.getLastColumn()).getValues();
ssDest.getRange(1,1,1, ssSource.getLastColumn()).setValues(titles);
// get the positive values you want from the cost col
var positiveValues = setPositiveCostValues(ssSource, ssDest, ssSource.getLastRow());
// fomrat the cells you want as percentage and set the color
formatCells(ssDest, positiveValues);
}
function setPositiveCostValues(ssSource,ssDest, lastRowSource){
var postiveCost = ssSource.getRange(2, 1, lastRowSource, 6).getValues();
// this loop will clean the empty elements and the ones that only have n/a
for (var i = postiveCost.length - 1; i >= 0; i--) {
if (postiveCost[i][0]) {
postiveCost.splice(i + 1, postiveCost.length - (i + 1));
postiveCost = postiveCost.filter(function(el){ return el != 'n/a'})
break;
}
}
return postiveCost;
}
function formatCells(ssDest, postiveCost){
var avgCpc = 4, cost = 6, row = 2, criteria = 0;
// iterate over the array and depending on the criteria format the cells
postiveCost.forEach(function(el){
if(el[cost - 1] > criteria){
var ssDestRange = ssDest.getRange(row, 1, 1, cost);
ssDestRange.setValues([el]);
ssDestRange.getCell(1, cost).setNumberFormat("##.#%");
// set the color depending on the avgCpc value condition
if(el[avgCpc - 1] < criteria) ssDest.getRange(row, avgCpc).setBackground('#d9ead3');
else ssDest.getRange(row, avgCpc).setBackground('#f4cccc');
row++;
}
});
}

How do I use a for-loop to add values to a JS variable? [duplicate]

This question already has answers here:
How to add two strings as if they were numbers? [duplicate]
(20 answers)
Closed 4 years ago.
I have made a piece of code that generates a random code of 12 characters. I am using Math.random and for-loops to do this. On the page you can write in an input how many codes you want.
What I want to do is save the generated codes in an array, however I can't do this because the for-loop and Math.random creates the code number by number and places them after each other. How can I add the whole 12 digit code to my array (so I can use it later)?
I've tried array.push with no luck. What works is outputting the numbers to DOM object in HTML, like this:
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
var result = document.querySelector("#result");
result.innerHTML += mathRandom;
}
But that doesn't put the 12 digit code into a variable. I've also tried this:
var codeNumber = "";
codeNumber += mathRandom;
But that ends up in the variable value having only 1 digit.
<input type="number" id="numberOfCodes">
<button onclick="codeGen()">Generate</button>
<div id="result"></div>
<script>
var numberOfCodes = document.querySelector("#numberOfCodes");
var arr = [];
function codeGen() {
x = numberOfCodes.value;
for (a = 0; a < x; a++) {
generate();
console.log("Generated code");
}
}
function generate() {
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
var result = document.querySelector("#result");
result.innerHTML += mathRandom;
}
}
</script>
</div>
</body>
</html>
I expect the codes created (after some changes) to be added to the array, so that I can later use the codes on the page. Each individual 12-digit code needs to have its own place in the array.
This should work:
var result = [], stringResult;
for (i = 0; i < 12; i++) {
var mathRandom = Math.floor(Math.random() * 9);
result.push(mathRandom);
}
stringResult = result.join(''); // concatenates all the elements
console.log(stringResult);
The problem with your code is that + sign attempts to determine types of the operands and to choose the right operation, concatenation or addition. When adding stuff to innerHtml it treats the number as string. That is why it worked.
You'll want to refactor things so generating a single code is encapsulated in a single function (generate() here), then use that function's output, like this. (I hope the comments are enlightening enough.)
var numberOfCodes = document.querySelector("#numberOfCodes");
var resultDiv = document.querySelector("#result");
function codeGen() {
var nToGenerate = parseInt(numberOfCodes.value);
for (var a = 0; a < nToGenerate; a++) {
var code = generate(); // generate a code
// you could put the code in an array here!
// for the time being, let's just put it in a new <div>
var el = document.createElement("div");
el.innerHTML = code;
resultDiv.appendChild(el);
}
}
function generate() {
var code = ""; // define a local variable to hold the code
for (i = 0; i < 12; i++) { // loop 12 times...
code += Math.floor(Math.random() * 9); // append the digit...
}
return code; // and return the value of the local variable
}
<input type="number" id="numberOfCodes" value=8>
<button onclick="codeGen()">Generate</button>
<div id="result"></div>
As this answer shows, this should work for you:
function makeRandCode() {
var code = "";
var ints = "1234567890";
for (var i = 0; i < 12; i++) {
code += ints.charAt(Math.floor(Math.random() * ints.length));
}
return code;
}
console.log(makeRandCode());
The problem is that you are adding numbers and what you really want is to concatenate them, the solution is to transform those numbers into String, then save them in the variable where you want to store them. An example:
2 + 2 = 4 and '2'+'2'='22'
Just use .toString() before save it in to the variable.

HTML input fields resetting when adding new input fields to list [duplicate]

This question already has answers here:
Javascript InnerHTML Erases Data In Form Elements
(4 answers)
Closed 7 years ago.
i'm working on a form and having some problems.
I have a input field that when changed to a higher or lower number it adds/removes extra fields but if type something into one of the extra fields and add an additional one it removes all values where it should be saving the values
js:
var eh = document.getElementById('ulId');//list to add list items to
var ac = document.getElementById('inputId');
var defaultId = "xin";
//x is short extra
var anyXFields = false;
var XF = 0;//extra fields
var lastXField = 0;
var mostFields = 0;
var index = [];
function hideInputFields(n) {
if (lastXField < n) {
return;
}else if(lastXField > n) {
$("#xin" + lastXField).hide();
}
}
//on change function
function oc() {
n = ac.value;
n = parseInt(n);
lastXField = n - 1;
if (n > 0) {
if (anyXFields) {
hideInputFields(n - 2);
}
for (i = 0; i < (n - 1); i++) {
if (i < mostFields) {
$("#xin" + i).show();
} else if (!index[i]) {
eh.innerHTML += '<li id="'+(defaultId + i)+'"><label>Højde' + (i + 2) +' <label class="sub">Height' + (i + 2) +'</label></label><input name="'+(defaultId + i)+'" class="inputfield"></li>';
index[i] = 1;
}
}
if (mostFields < (n - 1)) {
mostFields = n - 1;
}
anyXFields = true;
}
}
You are creating the list elements with innerHTML. Every time you run oc(), all of the list items are remade from the predefined string inside oc(), the data you have typed into the input elements are not saved to that string so they will be lost.
A solution/better way to achieve what you want to achieve is to use a for loop to create the list items and only remove the 'extra' input elements instead of remaking all of them on change.
If you are trying to append to a list and removing you can use .appendTo(...) or .remove in jquery rather than using inner html.

how to do loop depending how many arguments are in function [duplicate]

This question already has answers here:
JavaScript variable number of arguments to function
(12 answers)
Closed 8 years ago.
How to do autocreate variables(names,ssns and more), depending how many arguments we have in function, for each element i wanna have column(but i dont wanna to create it in manual mode) each column put in automatically created div, other column into second div. (i need to create table depending from xml tags, depending how many elements and tags it has)
function GetTableResult(checkername, position //(can be much more//) {
var xmlResponse = xmlHttp.responseXML;
root = xmlResponse.documentElement;
names = root.getElementsByTagName(checkername); //need to autocreate
ssns = root.getElementsByTagName(position);
var stuff = "";
for(var i=0; i<names.length; i++) {
stuff += names.item(i).firstChild.data + "<br/>";
}
var position = "";
for(var j=0; j<ssns.length; j++) {
position += ssns.item(j).firstChild.data + "<br/>";
}
theD = document.getElementById("theD");
theD.innerHTML = stuff;
theB = document.getElementById("theB");
theB.innerHTML = position;
}
use the arguments variable:
function func() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}

How to simplify this javascript [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Any way to simplify this code?
What is the best way to simplify this for going from 1-19?
var backer1 = document.getElementById("backer-prediction-1").value;
var incentive1 = document.getElementById("incentive-cost-1").value;
var totalIncentive1 = parseInt(backer1,10) * parseInt(incentive1,10);
document.getElementById("incentive-total-1").value = totalIncentive1;
var backer2 = document.getElementById("backer-prediction-2").value;
var incentive2 = document.getElementById("incentive-cost-2").value;
var totalIncentive2 = parseInt(backer2,10) * parseInt(incentive2,10);
document.getElementById("incentive-total-2").value = totalIncentive2;
Last one I posted they gave me a "for" loop.
Still learning this stuff.. Very New, THANKS!!!
Just like the last question, use a for loop:
for(var i = 1; i < 20; i++){
var backer = document.getElementById("backer-prediction-"+i).value;
var incentive = document.getElementById("incentive-cost-"+i).value;
var totalIncentive = parseInt(backer,10) * parseInt(incentive,10);
document.getElementById("incentive-total-"+i).value = totalIncentive;
}
for (var i=1; i<=19; i++) {
var backer = document.getElementById("backer-prediction-" + i).value;
var incentive = document.getElementById("incentive-cost-" + i).value;
var totalIncentive = parseInt(backer,10) * parseInt(incentive,10);
document.getElementById("incentive-total-" + i).value = totalIncentive;
}
This untested code should be enough, unless you need access to the backer and incentive values for each one of the cases after the loop is completed.
Use Array in javascript
var backer=[],
incentive=[],
totalincentive=[];
for(var i=1;i<20;i++){
backer[i] = document.getElementById("backer-prediction-"+i).value;
incentive[i] = document.getElementById("incentive-cost-"+i).value;
totalIncentive[i] = parseInt(backer[i],10) * parseInt(incentive[1],10);
document.getElementById("incentive-total-"+i).value = totalIncentive[i];
}
So you can use them after ending for loop , like
backer[1]....,backer[19]
incentive[1]....,incentive[19]
totalincentive[1]....,totalincentive[19]
If the value of backer and incentive is a number, I'd be tempted to do:
var get = document.getElementById;
var backer, incentive, totalIncentive = 0;
for(var i = 1; i < 20; i++) {
totalIncentive += get("backer-prediction-" + i).value * get("incentive-cost-" + i).value;
}
as the multiplication will implicitly convert numeric strings to numbers. But you really should validate that the content of those elements is a valid number before doing anything, even if using parseInt.

Categories