Simplify IF...Else Statement with For Loop [Javascript] - javascript

Context: I am creating a table of content to inform user of the page(s) that they have completed/visited.
The if...else statement below is working (by itself) but i want to generate the check for the other 30 chapters using the "i" counter from the FOR loop.
The script will first load the localstorage that contains the value 1 or 0 representing visited / unvisited and transfer that data onto variable chap1check. based on the result, the table of content should then show the user which page have or have not been visited.
im not sure of what i need to do but in theory, i will need to replace all the "chap1..." with the value of "i".
<script type="text/javascript">
var i;
for (i = 1; i <=31; i++){
var chap1Check = localStorage.getItem("chap1Status");
if(chap1Check == "1"){
document.getElementById('chap1Completion').innerHTML = "Completed";
}else{
document.getElementById('chap1Completion').innerHTML = "Incomplete";
}
}
</script>

Just concatenate the part of the string before the number with the number (i), followed by the part of the string after the number.
for (var i = 1; i <= 31; i ++){
var chapCheck = localStorage.getItem("chap" + i + "Status");
document.getElementById('chap' + i + 'Completion').textContent = chapCheck == "1" ? "Completed" : "Incomplete";
}

The following code will work. However, it would be much cleaner for you to just store an Array in local storage, and access it by indexing the array. Also, take a look into using the map functor over a for loop.
Note also that you should inline the declaration of i in the for loop as shown below. Otherwise, you may get conflicts with any future use of i in a for loop.
for (var i = 1; i <=31; i++){
var chapterChecker = localStorage.getItem("chap"+i+"Status");
if(chap1Check == "1"){
document.getElementById('chap'+i+'Completion').innerHTML = "Completed";
}else{
document.getElementById('chap'+i+'Completion').innerHTML = "Incomplete";
}
}

A solution using es6 template string could be this
for (var i = 1; i <=31; i++){
let content = '';
if(localStorage.getItem(`chap${i}Status`) == "1"){
content = "Completed";
}else{
content = "Incomplete";
}
document.getElementById(`chap${i}Completion`).innerHTML = content;
}

Related

Where/how to set increment on loop and update array only when condition found?

I'm writing a function to iterate through folders on Google Drive and match files (Google Sheets) with a variable string (a date specified on a table cell). When a matching file is found, the containing folder name string is assigned to folderItems[0] and the file URL to folderItems[1]. Once all matching files within a folder have been found, the next folder is iterated through in the same way. These "folderItems" arrays are stored in a parent array "folderItemsContainer" to create a 2 dimensional array which can then be output to a spreadsheet using .setValues().
I'm having trouble figuring out how or where to put the increment variable so that it will increment only when a filename match is made but not stop a loop when a match isn't found.
I've tried various structures including interchanging for and while loops and inserting if statements where seemingly useful. I've looked at a few different answers on Stackoverflow that come close to making sense but none seem to be applicable here. I'm fairly new to programming. I've got different variations of code I've tried, but this is where I'm up to so far:
function GetFolderData() {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet();
var currentYearPeriod = currentSheet.getRange("C1!A4").getValue();
// Logger.log(currentYearPeriod);
//Get folder objects from parent folder
var parentFolderId = "17F0fcBH0jmxsk2sUq723AuIY0E2G_u0m";
var parentFolder = DriveApp.getFolderById(parentFolderId);
//Get folders from specified parent folder
var StaffFolders = parentFolder.getFolders();
//Create container array
var folderItemsContainer = [];
//Create Item Array
var folderItems = [];
var i = 0;
//For every staff folder, regardless of content, do:
while (StaffFolders.hasNext()) {
//Get current folder object
currentFolder = StaffFolders.next();
//Get files in current folder object as FileIterator
FolderFiles = currentFolder.getFiles();
//If folder empty, outer while loop will iterate
if (FolderFiles !== null) {
//Iterate through existing files
while (FolderFiles.hasNext()) {
//Get file object sequentially
file = FolderFiles.next();
//When filename matches currentYearPeriod, store URL next to name in folderItems
for (i = 0; file.getName().indexOf(currentYearPeriod) !== -1; i++) {
folderItems[i] = [];
folderItems[i][0] = currentFolder.getName();
// Logger.log(currentFolder.getName());
folderItems[i][1] = file.getUrl();
folderItemsContainer[i] = folderItems[i];
}
}
}
}
return folderItemsContainer;
}
function InsertFolderData() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B4:Z1000").clearContent();
FolderData = GetFolderData();
Logger.log(FolderData);
sheet
.getRange(4, 2, FolderData.length, FolderData[0].length)
.setValues(FolderData);
Logger.log(FolderData);
/* var str = "";
for (var i = 0; i < FolderData.length; i++) {
str += FolderData[i] + "\r\n";
}
str = str.substr(0);
var ui = SpreadsheetApp.getUi();
ui.alert("DATA IMPORTED: " + "\r\n" + str);
*/
}
With the above code, I'm not entirely sure why but I seem to be getting stuck in an endless loop and the script doesn't finish. What I'm hoping to achieve is the folderItemsContainer array being populated with arrays containing file information (parent folder name[0] and file URL[1]) for files that match the currentYearPeriod variable. I've been refactoring the code and I've learned a lot but unfortunately not how to solve the problem.
You should check what's the deference between each loop, you are not fully undestending them. If you want to execute the instructions inside the for loop until a certain condition is met, in this case file.getName().indexOf(currentYearPeriod) !== -1, you should use a while loop. The bug is that the previous condition is never met because file never change while running the for loop. Thats why you are having an infinite loop. My solution:
// new variable
var cnt = 0;
while (StaffFolders.hasNext()) {
currentFolder = StaffFolders.next();
FolderFiles = currentFolder.getFiles();
if (FolderFiles !== null) {
while (FolderFiles.hasNext()) {
file = FolderFiles.next();
// You for loop started here
folderItems[cnt] = [];
folderItems[cnt][0] = currentFolder.getName();
folderItems[cnt][1] = file.getUrl();
folderItemsContainer[cnt] = folderItems[cnt];
// each time you read a new file you increment by 1
cnt++;
}
}
// this reset the counter for each new folder
cnt = 0;
}
Deferences between loops:
for loops
They are used when you know how many iteration will be needed. For example, if you want to print all the character of a string in the console:
const str = "hello";
for(let i = 0; i < str.length; i++) {
console.log(str.charAt(i));
}
let i = 0 is the starting point
i < str.length is when you want to stop. If you have to use a simbol which is not one of the follow <, <=, >, >=, you shouldn't be using a for loop.
i++ how you want to reach the stop property.
while loops
If you dont know when your loop is going to end, if it's going to have, 5 iteration, 100 iteration or 0 iteration. You should use while loops.
function getFirstL(str)
let i = 0;
while(i < str.length && str.charAt(i) !== "l"){
i++;
}
}
Your for loop. Here is syntax of for loop.
for (statement 1; statement 2; statement 3) {
// code block to be executed
}
Statement 1 is executed (one time) before the execution of the code block.
Statement 2 defines the condition for executing the code block.
Statement 3 is executed (every time) after the code block has been executed.
Your for loop doesn't define a condition for it to exit. A minimum or maximum value. something like
i<file.getName().indexOf(currentYearPeriod);
So it will check from 0-to that value.

Optimize function without looping through array

I've developed a small memory game, the game contains a function to flip cards for every turn. I'm looping thru the array containing the images every time to pick one card. I'm sure there's away to optimize this function so I don't have to loop thru the array every time in order to just turn one card. But I'm a bit clueless how to proceed and would like to get some guidance of how I possibly could optimize this function.
Can I create some sort of if statement perhaps? Note. Looking for vanilla javascript suggestions only and without creating an object to replace the array.
I'm clueless of another method then to loop thru the array.
function flipCards() {
var i; // Loopvar
// For loop
for (i = 0; i < cardsElems.length; i++) {
if (this == cardsElems[i]) {
if (cardsCounter > 1) {
return;
}
cardsElems[i].className = "brickFront";
cardsElems[i].src = "pics/" + allCards[i] + ".png";
removeListener(cardsElems[i], "click", flipCards);
if (cardsCounter < 1) {
card1 = i;
} else {
card2 = i;
}
cardsCounter += 1;
}
}
if (cardsCounter < 2) {
nextBtn.disabled = true; // Disable button
}
if (cardsCounter == 2) {
turns += 1;
turnNr.innerHTML = turns;
nextBtn.disabled = false; // Enable button
}
if (brickBack.length == 0) {
endGame();
}
} // End flipCards
how about using Loadash ?
It has plenty of function to manipulate arrays and very efficient.
you can store your data inside an object.By key(card name) value(fliped or not). then access by the key to change the value to be flipped or not.
example : {Ace: true} = {'cardName': 'isFlipped'}

JS loop - how to know if all additional conditions of the loop are fulfil

Comments are in code (I simply don't know how to check if ALL items meet the condition in the loop and if yes, perform an action):
// EDIT - more real life example.
Cause the working example is really complicated lets suposse we just... testing page links (or any html element on a page).
HTML:
<html>
<head>
<title>Links test</title>
</head>
<body>
<!-- links are random - we do not know how many they are, and how many of them got title attribute -->
google
facebook<
instagram
amazon
apple
</body>
</html>
JS:
let links = document.getElementsByTagName('a');
let linksLength = links.length;
let titleCount = 0;
for (i = 0; i < linksLength; i++) {
if (links[i].getAttribute('title') !== undefined) {
// I need to count all loop part that fulfil the condition
titleCount += 1;
// and if it's the last item that fulfil this condition then do something (in this main loop)
}
}
I would recommend using the filter method and then using the result to get the count and the last element.
Given an Array of Numbers data, here's an example:
relevantData = data.filter(e => [4,5,6].includes(e));
count = relevantData.length;
lastItem = relevantData.slice(-1)
let links = document.getElementsByTagName('a');
console.log('======================');
console.log('links HTML collection:\n');
console.log(links);
console.log('======================');
let linksLength = links.length;
let linksTitleCount = 0;
let html = '';
let linksArray = Array.from(links); // converting HTML collection to an array
let allLinksThatDoesHaveTitleAttr = linksArray.filter(function(l)
{
return (l.getAttribute('title') !== undefined && l.getAttribute('title') !== null && l.getAttribute('title') !== '');
});
let allLinksThatDoesHaveTitleAttrCount = allLinksThatDoesHaveTitleAttr.length;
console.log();
console.log('======================');
console.log('allLinksThatDoesHaveTitleAttribute:\n');
console.log(allLinksThatDoesHaveTitleAttrCount);
console.log('======================');
//let lastItem = relevantData.slice(-1)
for (i = 0; i < linksLength; i++) { // main loop throught ALL links
// lets supose we... build html element
html += 'link' + [i] +'\n';
if (links[i].getAttribute('title') !== undefined && links[i].getAttribute('title') !== null && links[i].getAttribute('title') !== '') { // condition that check existance of title attribute - thanks #m_hutley
linksTitleCount += 1; // this is cause I need to count all loop part that fulfil the condition
console.log(linksTitleCount);
console.log();
html += 'this link - ' + [i] + ' - does not have title attribute! \n\n';
if (linksTitleCount == allLinksThatDoesHaveTitleAttrCount)
{
console.log('DONE!'); // we know we are done - we loop throught all elements that does have title attr
// NOW if it's the last item that fulfil this condition then do something (in this main loop) + some async operations that will leater (when data arrive) target specific HTML with specific link (order like in loop - [i])
}
}
}
console.log('HTML result is:\n');
console.log(html);

Populate a prompt with elements of an array and number them off

(Stack Overflow doesn't have a tag for 'prompt' so I have used alert as I am guessing it is similar enough to attract the right answerers.)
Hello,
I am currently making a JavaScript-based game for an assignment at university. I am usually pretty good with problem solving but have been stumped by this issue.
To explain, I have an array which names the possible armour slots the player can pick. In any order these can be picked, and each time the choice gets pushed to a second array which handles what has already been picked (and in what order) and that item gets spliced from the original array. There is a while loop which runs through until all 3 have been picked.
var armourSlotToPick = ["Head", "Chest", "Legs"],
armourSlotPicked = [],
armourLoop = 1,
indexArmour = 0;
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
function armour() {
while (armourLoop < 4) {
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
if (armourPick == 1) {
armourSlotPicked.push(armourSlotToPick[0]);
armourSlotToPick.splice(0,1);
} else if (armourPick == 2) {
armourSlotPicked.push(armourSlotToPick[1]);
armourSlotToPick.splice(1,1);
} else if (armourPick == 3) {
armourSlotPicked.push(armourSlotToPick[2]);
armourSlotToPick.splice(2,1);
} else {
alert("Invalid choice, you suck");
break;
}
armourLoop++;
}
}
I know it probably wouldn't be possible to do the whole return in numInArray() to the prompt, but it shows some working.
Now the problem: I got it working so that each item in the array was numbered (var armourSlotToPick = ["1. Head", "2. Chest", "3. Legs"],) but as you could see, if the player chose 2, then the next time it would show "1. Head (new line) 3. Legs" and when the player chooses 3, a problem would occur, as they were really meant to choose 2. How is it possible to number the items in the array, in a prompt?
I'm possibly over thinking this but I have suffered for a few hours now.
I thank you in advance for any insight you may have,
Daniel.
EDIT: Solved.
Below is the end result, a slight variation from the edited answer from Jonathan Brooks.
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
var armourLoop = 1;
function armour() {
while (armourLoop < 4) {
var message = "Pick an armour slot to generate an item for:\n";
for (var i = 0; i < armourSlotToPick.length; i++) {
if (armourSlotToPick[i] !== null) {
message += "" + i + ". " + armourSlotToPick[i] + "\n";
}
}
var armourPick = prompt(message);
if (armourPick > armourSlotToPick.length-1 || armourPick < 1) {
alert("Invalid choice, you suck");
} else {
var insert = armourSlotToPick.splice(armourPick, 1);
armourSlotPicked.push(insert);
}
armourLoop++;
}
armourSlotPicked.splice(0,1);
}
armour();
alert(armourSlotPicked.join("\n"));
I thank all that have contributed to this discussion and the end result, and I hope this is a good example for future problems people may have similar to this.
Check out my fiddle, I think I have a working solution.
What you really want to be using are Object Literals with your own indexing (starting from 1) - if it were me, I would create my own way to iterate over this custom indexing by adding a method to the Object's prototype, but I digress.
You're overcomplicating your code by using a while loop, and that large bulk of if statements is unnecessary: instead, all you need is some basic validation on the input and then you can just trust whatever input passes this validation. That is demonstrated here:
if ( armourPick > armourSlotToPick.length || armourPick < 1 ) {
alert("Invalid choice, you suck");
}
else {
armourSlotPicked.push( armourSlotToPick[armourPick-1] )
alert (armourSlotPicked[armourSlotPicked.length-1].value);
}
Read my code carefully, and you should get a better understanding of how to deal with certain issues.
EDIT:
As per your request, I think I have a solution that suits your needs. Basically all you have to do to have the arrays "start" at an index of 1 is to fill the zeroth element with a null value, like so:
var armourSlotToPick = [null, "Head", "Chest", "Legs"]
var armourSlotPicked = [null];
You just have to remember to take this null object into account in your code, for example:
if (armourSlotToPick[i] !== null) {
message += "" + i + "\n";
}
The indices will update automatically. See this updated fiddle for more details.
use structures / objects as content in the array, instead of just values.
the basic concept:
armourSlotPicked.push({ "key": 1, "value":armourSlotToPick[1]})
alert("value: " + armourSlotPicked[0].value)
alert("key: " + armourSlotPicked[0].key)
edit: responding to comments can take some space.
IMHO a prompt is the completely wrong tool for this, since most browsers would ask the user permission to prevent multiple popups, and since a promt can only return 1 piece of information, you can only ask for 1 thing per popup. Instead you ought to use a div element, with checkboxes for each information..
That being said it can easily be used in a promt.
The prompt is just a built in function, that takes a string as an argument (which is shown as text in the popup) and returns a string with the users input.
what does the magic for you is in fact this:
array.foreach(): The forEach() method executes a provided function once per array element.
in your case that means it calls a function that returns a string for each element in the array, and concatenates the strings.
in the old days you would have written this:
var messageText= "Pick an armour slot to generate an item for:\n"
for(var i = 1; i < armourSlotToPick.length; i++){
messageText += i + ". " + armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
but in this modern age, you define a printing function, and use it to generate the loop:
function numInArray() {
indexArmour++;
return (indexArmour + ". " + armourSlotToPick[indexArmour - 1] + "\n");
}
//more code before we get to where the function is used....
indexArmour = 0;
var messageText = "Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray);
var armourPick = prompt(messageText);
or in a single line as in your code:
indexArmour = 0; //you forgot this - otherwise the list will only be complete once?
var armourPick = prompt("Pick an armour slot to generate an item for:\n" + armourSlotToPick.forEach(numInArray));
It produces the same output, because it does the same thing, its just written very differently!
If the array holds "object literals" instead of simply values, as I suggest, the old fashioned code would look something like this:
function contains(a, value) {
try{
for (var i = 0; i < a.length; i++) {
if (a[i].value == value) {
return true;
}
}
}
catch(err) {
// do nothing
};
return false;
}
and later..
for(var j = 0; j < 4; j++){
for(var i = 0; i < Math.min(armourSlotToPick.length); i++){
if( contains(armourSlotPicked, armourSlotToPick[i- 1]) )
continue;
var messageText = "Generate an item for armour in slot: " + i + "\n"
messageText += armourSlotToPick[i- 1] + "\n";
}
var armourPick = prompt(messageText);
if (armourPick > 0 && armourPick < armourSlotToPick.length) {
armourSlotPicked.push({"key":j, "value":armourSlotToPick[armourPick]);
}
...
}
//now we have an array that holds information about when what was picked..
or something along those lines.. this is bt.w completely untested, it's just for illustration
You want to use the array index to number your items. Since your numbers are one-based and the index is zero-based, you will need to convert between the two when outputting and interpreting the response.
This approach will also allow you to eliminate all but two of the cases in your if-else statement.

.length on array crashing when length is 1 (maybe issue with split)

I'm having trouble with this code. I've tried to troubleshoot it many times and seem to have isolated the issue, but can't figure out the cause.
If the variable called string is set to something in the form of "text v. text," the code runs fine and the first if-statement triggers the sentence. If the string contains text but no "v." i.e. nothing that meets the search separator value, the function fails and does not execute the second if-statement.
Link to Fiddle: http://jsfiddle.net/qsq4we99/
Snippet of code, there also would need to be a html div with ID "outputtext."
function brokenCode()
{
//Setting Relevant Variables
var string = "red";
var array = string.split("v.");
var number = array.length;
// Checking location of things
var findText1 = array[0].search("search text");
var findText2 = array[1].search("search text");
//Running Conditional Stuff
if(number > 1)
{
document.getElementById('outputtext').innerHTML = "2+ listed";
}
else if(number < 2)
{
document.getElementById('outputtext').innerHTML = "1 listed";
}
}
brokenCode();
In this simplified example there is no clear explanation why the search operations need to occur (they are there because in the real code they are needed... but something about them seems to be causing the problem (even in this simple example). If the two searches are removed, the code runs smoothly.
You can't start setting variables from the array without checking for length. Before setting findText1 & findText2, check to make sure the length of the array is greater than zero.
function brokenCode() {
//Setting Relevant Variables
var string = "red";
var array = string.split("v.");
var number = array.length;
if (number > 0) {
// Checking location of things
var findText1 = array[0].search("search text");
var findText2 = array[1].search("search text");
//Running Conditional Stuff
if(number > 1)
{
document.getElementById('outputtext').innerHTML = "2+ listed";
}
else if(number < 2)
{
document.getElementById('outputtext').innerHTML = "1 listed";
}
}
}
brokenCode();

Categories

Resources