Google Spreadsheets Script Keeps Writing Empty Strings - javascript

I have a Google form that gets submitted to a Google spreadsheet. On the spreadsheet side, I have a Google Apps Script that is supposed to encrypt the submitted password. But for whatever reason, it writes an empty string to where the encrypted password should be. This is really starting to stress my out. Here is my code:
function encryptPassword(e) {
var password = e.values[6];
var split = password.split("");
password = "";
var char;
for(char in split) {
password = password.concat(getBinary(split[char]));
}
var spreadsheet = SpreadsheetApp.openById("1CywforbyBmPDHt2Uw9lJtyhqeklAAJp0IG7GfVV6U5U");
spreadsheet.getRange("G" + spreadsheet.getLastRow().toString()).setValue(password);
spreadsheet.getRange("H" + spreadsheet.getLastRow().toString()).setValue(password);
}
function getBinary(char) {
var binary = "";
var numValue;
var range;
var value;
var chars = [
["#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P"],
["#", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
["&", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"],
["%", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"],
["$", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
];
for(range in chars) {
for(value in range) {
if(value > 0) {
if(char == chars[range][value]) {
numValue = value - 1;
binary = binary + chars[range][0];
if(numValue / 8 >= 1) {
numValue = numValue - 8;
binary = binary.concat("1");
} else {
binary = binary.concat("0");
}
if(numValue / 4 >= 1) {
numValue = numValue - 4;
binary = binary.concat("1");
} else {
binary = binary.concat("0");
}
if(numValue / 2 >= 1) {
numValue = numValue - 2;
binary = binary.concat("1");
} else {
binary = binary.concat("0");
}
if(numValue / 1 >= 1) {
binary = binary.concat("1");
} else {
binary = binary.concat("0");
}
}
}
}
}
return binary;
}
The encryptPassword(e) function is set to run whenever a form is submitted to the spreadsheet. Also please be aware that I have modified the contents of the chars array in an attempt to keep my encryption private. This shouldn't make a difference though since the rest of the code stays the same.
How do I fix my script so it actually writes an encrypted password to the spreadsheet rather than an empty string?

You've got two For/In loops:
for(range in chars) {
for(value in range) {
A For/In loop is meant to loop through the properties of an object. When you use it to loop through an array, like you are doing, the "property" is the index number of the array.
So the line:
for(range in chars) {
Is causing the variable range to be a number value on every loop. On the first loop, the value of range is zero.
The second loop:
for(value in range) {
is never looping. There is nothing to loop through. The value of the variable range is just a single number. You can't loop through a single number. If you use the debugger, you can watch what every line is doing, and execute one line of code at a time.
If you want to get the index position of one of the characters in the password, you could use indexOf(). For example, if the character in the password was the letter "i".
var indexPosition = chars[2].indexOf(char);
The value of indexPosition would be 9. The third array in the outer array chars has an element "i" in the 9th index position.

Related

Converting nested loops into forEach();

Im trying to learn forEach() method but i cant find more advanced examples. So i thought about refactoring my Codewars code to learn from it. I dont know know to properly use forEach method in nested loops. Hope You can help me learn from this example :)
6 kyu - Replace With Alphabet Position
https://www.codewars.com/kata/546f922b54af40e1e90001da/train/javascript
function alphabetPosition(text) {
let textToArray = text.replace(/[^a-zA-Z]/gi,'').toUpperCase().split(''); //Eliminate anything thats not a letter
const alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
let pointsHolder = []; //empty array for score
for (let i = 0; i < textToArray.length; i++){
for (let j = 0; j < alphabet.length; j++) {
if (textToArray[i] == alphabet[j] ) { //We check the index of given string letter in alphabet
pointsHolder.push(j+1) //give it a score based on place in alphabet(+1 for 0 as 1st index)
}
}
}
return pointsHolder.join(' '); //return scored array as a string with spaces
}
There is really no need to use a nested loop, which is computationally expensive. With that, you also don't have to manually create an A-Z array.
You can easily convert alphabets to any arbitrary number using String.charCodeAt(). a has a character code of 97, b has a character code of 98, and etc... to get a one-based index (a=1, b=2, ...) you jus t need to subtract 96 from the number.
function alphabetPosition(text) {
const alphabets = text.toLowerCase().replace(/[^a-z]/g, '').split('');
return alphabets.map(alphabet => alphabet.charCodeAt(0) - 96).join(' ');
}
Alternatively you can also use a for...of loop, but that requires storing the array in yet another variable before returning it:
function alphabetPosition(text) {
const alphabets = text.toLowerCase().replace(/[^a-z]/g, '');
const codes = [];
for (const alphabet of alphabets) {
codes.push(alphabet.charCodeAt() - 96);
}
return codes.join(' ');
}
(Note: #Terry's solution is still the more efficient solution to your code challenge)
You can replace it in the following way:
function alphabetPosition(text) {
let textToArray = text.replace(/[^a-zA-Z]/gi, '').toUpperCase().split('');
const alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
let pointsHolder = [];
textToArray.forEach(t2a => {
alphabet.forEach((a, j) => {
if (t2a == a) { pointsHolder.push(j + 1) }
})
})
return pointsHolder.join(' ');
}
console.log(alphabetPosition("ABCSTU"))
An alternative to the charCode solution proposed in Terry's answer but which also avoids nested loops is be to create a Map of the characters you want to score against and then access it per character from the passed string.
Keep in mind that strings are iterable without needing to convert to an array.
function alphabetPosition(text) {
text = text.toUpperCase().replace(/[^A-Z]/gi, '');
const alphabet = new Map(
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
.map((v, i) => [v, i + 1])
);
const pointsHolder = [];
for (const char of text) {
pointsHolder.push(alphabet.get(char))
}
return pointsHolder.join(' ');
}
console.log(alphabetPosition("AB😬CS🐹TU"))
This also allows you to use a Map which doesn't necessarily have consecutive charCodes
function alphabetPosition(text) {
text = text.toUpperCase().replace(/[^😬🐹🤓]/gi, '');
const alphabet = new Map(
["😬", "🐹", "🤓"]
.map((v, i) => [v, i + 1])
);
const pointsHolder = [];
for (const char of text) {
pointsHolder.push(alphabet.get(char))
}
return pointsHolder.join(' ');
}
console.log(alphabetPosition("AB😬CS🐹TU"))

How to scroll forwards and backwards in a javascript array

I am new to javascript and would really appreciate some help. The javascript below is connected to two buttons in an an html documented called "next" and "previous". The buttons are intended to allow a user to scroll forwards and backwards through an array that contains the letters of the alphabet. The code is almost working, but has one bug. Clicking the "next" button will display the next letter in alphabet (i.e. B will switch to C). However, clicking the "previous" button will display the next letter in the alphabet before changing directions. For example, if the letter "C" is displayed, clicking "previous" will display "D" and then "C", "B", "A", "Z", etc...
What changes need to be made to the code below to fix this problem? If a letter is displayed, I would like for the function "next" to return the next letter and for the function "previous" to return the letter that comes immediately before the letter being displayed.
function next(){
let upperCase = ["A","B", "C", "D", "E","F", "G", "H", "I","J", "K", "L", "M","N", "O", "P", "Q","R", "S", "T", "U","V", "W", "X", "Y","Z"];
document.querySelector(".item-2").innerHTML = upperCase[index++];
if(index > upperCase.length - 1)
index = 0;
}
function previous(){
let upperCase = ["A","B", "C", "D", "E","F", "G", "H", "I","J", "Q", "L", "M","N", "O", "P", "Q","R", "S", "T", "U","F", "W", "X", "Y","Z"];
document.querySelector(".item-2").innerHTML = upperCase[index--];
if(index < 0)
index = upperCase.length - 1;
}
You need to set ++ and -- before the index variable in order to return its value after incrementing it:
let index = 0;
const previousButton = document.getElementById('previous');
const nextButton = document.getElementById('next');
previousButton.addEventListener('click', previous);
nextButton.addEventListener('click', next);
const upperCase = ["A","B", "C", "D", "E","F", "G", "H", "I","J", "K", "L", "M","N", "O", "P", "Q","R", "S", "T", "U","V", "W", "X", "Y","Z"];
function next(){
if(++index > upperCase.length - 1)
index = 0;
document.querySelector(".item-2").innerHTML = upperCase[index];
}
function previous(){
if(--index < 0)
index = upperCase.length - 1;
document.querySelector(".item-2").innerHTML = upperCase[index];
}
<div class="item-2"></div>
<button id="previous">Previous</button>
<button id="next">Next</button>

How to validate whether an array contains values from other arrays that are chosen by the user?

I'm trying to validate an array against user-selected criteria which are four other arrays of: uppercase, lowercase, numbers, or special characters.
My passwordPool array combines the arrays that they chose to include in their password and then from there values are added to a final array in random order.
From there I am trying to validate whether all of the user-chosen requirements have been met and if not loop back through the password generator until they've been met.
I've looked at trying to compare the window.confirm responses to the final password, to compare the arrays to get confirm whether they have what they need, I've tried include()... I'm really not sure how to do this.
// Write password to the #password input
function writePassword() {
var password = generatePassword();
var passwordText = document.querySelector("#password");
passwordText.value = password;
}
// Add event listener to generate button
generateBtn.addEventListener("click", writePassword);
//Generate password
function generatePassword() {
var pwLength;
var confirmLower;
var confirmUpper;
var confirmSpecial
var confirmNumber;
//finding out how many characters the user wants in the password
var pwLength = parseInt(prompt("Enter a number between 8-128"));
while (pwLength < 8 || length > 128 || pwLength == NaN || pwLength === null) {
alert("That's not a number. Please enter a number from 8-128.");
pwLength = prompt("Enter a number between 8-128");
};
//Defining variables
var confirmLower = confirm("Do you want to include lower case characters?");
var confirmUpper = confirm("Do you want to include upper case characters?");
var confirmNumber = confirm("Do you want to include number characters?");
var confirmSpecial = confirm("Do you want to include special characters?");
//If all answers are false, looping back through to get atleast one true response.
while (confirmLower === false && confirmUpper === false && confirmNumber === false && confirmSpecial === false) {
alert("You need to select one type of character");
var confirmLower = confirm("Do you want to include lower case characters?");
var confirmUpper = confirm("Do you want to include upper case characters?");
var confirmNumber = confirm("Do you want to include number characters?");
var confirmSpecial = confirm("Do you want to include special characters?");
}
// Various Arrays
var lowerCaseChar = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
var upperCaseChar = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
var numericChar = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
var specialChar = ["!", "#", "#", "$", "%", "^", "&", "*", "(", ")", "{", "}", "|", "[", "]", ";", "'", ":", "<", ">", "?", "/"];
var passwordPool = [];
var randomPassword = [];
//loop through chosen options to create a password
function generateChar() {
if (confirmLower) {
passwordPool.push(...lowerCaseChar);
}
if (confirmUpper) {
passwordPool.push(...upperCaseChar);
}
if (confirmNumber) {
passwordPool.push(...numericChar);
}
if (confirmSpecial) {
passwordPool.push(...specialChar);
}
}
//create an array thats a pool for all selected values from char arrays
generateChar();
console.log(passwordPool);
//create a final for loop that math.random.floors the array . the length of pwLength that meets the criteriaselected by user
function pushChar() {
for (var i = 0; i < pwLength; i++) {
var item = passwordPool[Math.floor(Math.random() * passwordPool.length)];
randomPassword.push(item);
}
pushChar();
console.log(randomPassword);
//at the end validate that all of the conditions were met.
} ```

random generator function javascript

I'm making a guessing game for class. I pretty much have it done, but the biggest problem I've had was making the string randomize itself after the game ends. It picks a random letter if the page refreshes but the objective is to keep track of stats so refreshing is out.
Here's my javascript code:
var lettersChar = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
function randomFunc() {
var randomMath = lettersChar[Math.floor(Math.random() *
lettersChar.length)];
return randomMath;
}
//variables
var randomChar = randomFunc();
//array where user input will be stored
var userChoices = [];
var wins = 0;
var losses = 0;
var guesses = 9;
//keypress event
document.onkeyup = function (event) {
var userGuess = event.key;
var userOptions = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
"z"];
//if guess is correct, +1 to win and refresh game and generate new letter
if (userOptions.indexOf(userGuess) > -1) {
if (userGuess === randomChar) {
wins++;
guesses = 9;
randomFunc();
console.log(wins);
}
//if guess is incorrect, then print wrong guess and -1 to guesses left
else if (userGuess !== randomChar) {
guesses--;
userChoices.push(userGuess);
}
//If guess reaches to 0 add +1 to loss and restarts game
if (guesses === 0) {
losses++;
guesses = 9;
userChoices = [];
randomChar;
}
}
I've tried invoking the function and it doesn't appear to work for me at this moment. Any help is appreciated!
See the last line you posted:
randomChar;
You're just declaring randomChar again and not doing anything with it. If you want to randomize it after the game ends, you should call randomFunc again:
randomChar = randomFunc();
Same for when the user wins: replace with
if (userGuess === randomChar) {
wins++;
guesses = 9;
randomChar = randomFunc();
console.log(wins);
}

manually sorting a paragraph by made-up alphabet with javascript

I'm trying to sort a paragraph alphabetically, not according to the normal ABC but a made-up one (var order).
I wrote this function and it works great, but only for the first letter of each word - not in-word sorting as well (for example, in correct ABC 'banana' would come before 'birthday').
I'm not sure where to go from here.
$("#send").click(function () {
var text = $("#text").val().replace(/[^A-Za-z0-9_\s]/g, "").toUpperCase().split(" ");
var order = ["Q", "B", "K", "D", "H", "V", "Z", "E", "F", "O", "G", "L", "M", "S", "N", "P", "I", "X", "A", "R", "W", "U", "C", "J", "T", "Y"];
var i, t, j;
var newText = []; // will hold the new alphabet
// function to sort the words:
for (i = 0; i < order.length; i++) {
for (t = 0; t < text.length; t++) {
var firstChar = text[t][0];
if (order[i] == firstChar) {
newText.push(text[t]);
}
}
}
console.log(newText.join(','));
});
EDIT:
An example input can be: "Hi dan don't you think that this is awesome",
and I want the output to be: "don't dan hi is awesome this think that you".
You could use an object with the index of the letters and use Array#sort with a callback which looks for every letter adn calculates the order.
function foo(text) {
var text = text.replace(/[^A-Za-z0-9_\s]/g, "").toUpperCase().split(" "),
order = "QBKDHVZEFOGLMSNPIXARWUCJTY",
ref = {};
order.split('').forEach(function (a, i) { ref[a] = i + 1; });
text.sort(function (a, b) {
var i = 0, v;
do {
v = (ref[a[i]] || 0) - (ref[b[i]] || 0);
i++;
} while (!v)
return v;
});
console.log(text.join(', '));
}
foo('a aa ab b ba bb');
foo('banana birthday');
The problem with your algorithm is that it only compares the first letter in each word, but if the letters are the same the algorithm needs to compare the next letter in each word. Here's a solution that uses recursion:
function doSort(inputArr) {
var words = inputArr.slice(0);
var alphabet = ["Q", "B", "K", "D", "H", "V", "Z", "E", "F", "O", "G", "L", "M", "S", "N", "P", "I", "X", "A", "R", "W", "U", "C", "J", "T", "Y"];
words.sort(function(item1, item2) {
return sortRecursive(item1, item2, 0);
});
function sortRecursive(item1, item2, idx) {
if (item1.length <= idx && item2.length <= idx) {
return 0;
} else if (item1.length <= idx) {
return -1;
} else if (item2.length <= idx) {
return 1;
} else if (item1[idx] == item2[idx]) {
return sortRecursive(item1, item2, idx+1);
} else {
return alphabet.indexOf(item1[idx].toUpperCase()) - alphabet.indexOf(item2[idx].toUpperCase());
}
}
return words;
}
var arr = ["banana", "quebec", "bird", "birthday", "birdman", "bird"];
var sortedArr = doSort(arr);
console.log('unsorted',arr);
console.log('sorted', sortedArr);
https://jsfiddle.net/2qgaaozo/

Categories

Resources