Related
I am new to javaScript and having a terrible time with understanding loops.
Currently I have a random text generator function. I have gotten it to generate random characters from a string but only one character each time the function is ran. I would like to build a string of 20 random characters from the output of the function.
This is what I have so far:
function passGen() {
let text = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVVWYXZ()!##$%&*/?+-%".split("");
let randomIndex = Math.floor(Math.random() * text.length);
return text[randomIndex];
}
let randomPass = passGen();
I would like to let the function produce the random character and then use a for loop to out put 20 of these random characters in a string.
I can't get
for (let i = 0; i <= 20; i++){
console.log(randomPass)
to work.
Any help is appreciated.
You need to define a string variable and then use a loop to append characters to it, like this:
function passGen () {
const text = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVVWYXZ()!##$%&*/?+-%'.split('')
let res = ''
for (let i = 0; i < 20; i++) {
res += text[Math.floor(Math.random() * text.length)]
}
return res
}
const randomPass = passGen();
You need to specify for yourself what characters you want to support, for example:
function getRandomCharacter(supportedCharacters) {
return supportedCharacters[parseInt(Math.random() * supportedCharacters.length)];
}
function getRandomString(supportedCharacters, length) {
let output = "";
while (length--) output += getRandomCharacter(supportedCharacters);
return output;
}
console.log(getRandomString([
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'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',
], 20));
getRandomCharacter randomizes a character
Math.random() generates a real number which is greater or equal than 0 but smaller than 1
multiplying Math.random() with the number of supported characters scales the value to the interval between [0, length)
parseInt rounds the result down
the result of parseInt can be used as an index
getRandomString generates all the characters using a while loop
length-- evaluates length and then decreases it by 1
+= concatenates the resulting character to output
return output; clarifies that the result of the function is the string
the test sample defines an array (you could do it with a string as well if you want) of the characters you want to support and the length of the string you desire
I'm struggling with the logic to do the Recursion.
For the grid below I need to search a word and return an array of indexes that form the word if it exists or an empty array [] if it doesn't exist.
The word may start anywhere in the grid, and consecutive letters can be either immediately below or immediately to the right of the previous letter.
grid = [
['c', 'c', 'x', 't', 'i', 'b'],
['c', 'c', 'a', 't', 'n', 'i'],
['a', 'c', 'n', 'n', 't', 't'],
['t', 'c', 's', 'i', 'p', 't'],
['a', 'o', 'o', 'o', 'a', 'a'],
['o', 'a', 'a', 'a', 'o', 'o'],
['k', 'a', 'i', 'c', 'k', 'i'],
];
word = "catnip"
find_word_location(grid, word)
// OUTPUT [ (1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 4) ]
This is what I have so far but it's not working.
function find_word_location (grid, word) {
const dfs = (i, j, wordIndex, res) => {
if (wordIndex == word.length) return;
if (
i > grid.length - 1 ||
j > grid[0].length - 1 ||
grid[i][j] !== word[wordIndex]
)
return;
if (grid[i][j] == word[wordIndex]) {
res.push(`(${i},${j})`);
grid[i][j] = "#";
}
dfs(i + 1, j, wordIndex + 1, res);
dfs(i, j + 1, wordIndex + 1, res);
grid[i][j] = word[wordIndex];
return res;
};
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] == word[0]) {
let result = dfs(i, j, 0, []);
return result;
}
}
}
return [];
}
Some of the issues:
After the initial call of dfs your code always returns the result. This means that it assumes that if the first letter matches, the whole word must be matched starting at that cell. But this is not true. It might not find a match there, while there might be other places in the grid with that first letter which do give a full word match. So this return statement should be conditional (only when there is success).
The array referenced by res only grows. It never shrinks. Realise there is only one res array -- referenced by all res variables that live in the dfs execution contexts. So all partial matches (that eventually fail to lead to a full match) are collected in it, and concatenated one after the other. There is no backtracking applied to it.
I find it more elegant to actually only start building an array when the whole word has been matched, and then fill up an array while getting out of the recursive calls (as opposed to doing this while deepening the recursion).
The recursive calls of dfs completely ignore the values that those calls return, so no distinction is made between failure and success. You'd need to check the result of the first recursive call and if it was successful, the second one should not even be made.
Not a problem, but the condition in if (grid[i][j] == word[wordIndex]) { will always be true, given you already tested the opposite condition in the preceding if statement.
Here is a correction to your code, also implementing the idea I expressed in the second point, i.e. only filling up an array when it is already known there was a success:
// No res argument. res will be populated "inside-out" via the return value
function find_word_location (grid, word) {
const dfs = (i, j, wordIndex) => {
if (wordIndex == word.length) return []; // Start a solution with this res
if (
i > grid.length - 1 ||
j > grid[0].length - 1 ||
grid[i][j] !== word[wordIndex]
)
return;
grid[i][j] = "#";
// Use the returned value
// and apply short-circuit to avoid useless second call
const res = dfs(i + 1, j, wordIndex + 1) || dfs(i, j + 1, wordIndex + 1);
grid[i][j] = word[wordIndex];
if (res) res.unshift([i, j]); // Extend the path we got from recursion
return res;
};
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] == word[0]) {
let result = dfs(i, j, 0, []);
if (result) return result; // conditionally
}
}
}
return [];
}
var grid = [
['c', 'c', 'x', 't', 'i', 'b'],
['c', 'c', 'a', 't', 'n', 'i'],
['a', 'c', 'n', 'n', 't', 't'],
['t', 'c', 's', 'i', 'p', 't'],
['a', 'o', 'o', 'o', 'a', 'a'],
['o', 'a', 'a', 'a', 'o', 'o'],
['k', 'a', 'i', 'c', 'k', 'i'],
];
var word = "catnip";
var result = find_word_location(grid, word);
console.log(result);
This approach does not hand over the result to the recursive call.
The function takes grid, the word or the left over word part, and the actual indices, which has zero as default value.
Inside, the exit condition comes first by checking boundaries.
Then a check for the wanted character follows.
Inside it check with new word length, without actual character and if an empty string, it returns the indices.
The rest is nearly identically without the part for checking the found indices and if they are adjacent to the actual elememnt.
Roughly, get sub indices, check if there are indices and return the result either with actual indices (letter match) or just the rest.
const
findWord = (grid, word, i = 0, j = 0) => {
const isAdjacent = (x, y) => x === i && y === j + 1 || x === i + 1 && y === j;
let sub;
if (i + 1 === grid.length || j + 1 === grid[i].length) return [];
if (grid[i][j] === word[0]) {
const w = word.slice(1);
if (!w.length) return [[i, j]];
sub = findWord(grid, w, i + 1, j);
if (sub.length && isAdjacent(...sub[0])) return [[i, j], ...sub];
sub = findWord(grid, w, i, j + 1);
if (sub.length && isAdjacent(...sub[0])) return [[i, j], ...sub];
}
sub = findWord(grid, word, i + 1, j);
if (sub.length) return sub;
sub = findWord(grid, word, i, j + 1);
if (sub.length) return sub;
return [];
},
grid = [['c', 'c', 'x', 't', 'i', 'b'], ['c', 'c', 'a', 't', 'n', 'i'], ['a', 'c', 'n', 'n', 't', 't'], ['t', 'c', 's', 'i', 'p', 't'], ['a', 'o', 'o', 'o', 'a', 'a'], ['o', 'a', 'a', 'a', 'o', 'o'], ['k', 'a', 'i', 'c', 'k', 'i']],
word = "catnip",
result = findWord(grid, word);
console.log(result.map(p => p.join('-')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
For example say you have the string 'ab?d?f' and you must grab the string and replace it with any random letters in the '?' like 'abcdef' or 'abjdlf' but it cannot be 'abbdef' or 'abcdff'.
I have attempted this below:
const letters = ['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'];
const randomLeter = () => letters[Math.floor(Math.random() * 26)];
const riddle = 'ab?d?f';
let pickedLetter;
let solvedRiddle;
function solve(riddle) {
for (let i = 0; i < riddle.length; i++) {
if (riddle[i] === '?' && !(riddle[i-1] === randomLeter) && !(riddle[i+1] === randomLeter)){
console.log('Changing to letter');
solvedRiddle = riddle.replace('?', pickedLetter);
}
pickedLetter = randomLeter();
console.log(i, riddle[i], pickedLetter);
}
return solvedRiddle;
}
// The above only returns the first '?' changed but leaves the second one unchanged ... ??? Why can I not change the value of solvedRiddle a second time inside the loop? I can see by my log that it reads at true, but the value won't be re-written.
console.log(solve(riddle));
function solve(riddle) {
for (let i = 0; i < riddle.length; i++) {
if (riddle[i] === '?'){
console.log('Changing to letter');
let pickedLetter = randomLeter();
while (riddle[i-1] === pickedLetter || riddle[i+1] === pickedLetter) {
pickedLetter = randomLeter();
}
riddle = riddle.replace('?', pickedLetter);
}
}
return riddle;
}
Also, it's because u update and return solvedRiddle. Your original riddle string is not updated, so in the second run, it is still changing the first ?
I modified your code so that it suits the needs, however there may be some bugs or exceptions not handled yet, but it can give you a hint that how you are going to solve the problem.
const letters = ['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 randomLeter = (excluded) =>
{
let letter = false;
do
{
letter = letters[Math.floor(Math.random() * 26)];
}
while(excluded.includes(letter));
return letter;
}
let getIndices = (s, t) => {
return [...s].flatMap((char, i) => (char === t ? i : []));
};
let Solve = (riddle) =>
{
riddle = [...riddle];
//get the locations of all ? in the string
let all = getIndices(riddle, '?');
for(let i = 0; i < all.length; i++)
{
//exluding characters before and after the ?
let excluded = [riddle[all[i] - 1], riddle[all[i] + 1]];
riddle[all[i]] = randomLeter(excluded);
}
return riddle.join("");
};
let riddle = 'ab?d?f';
document.getElementById('riddle').innerHTML = 'The Riddle ' + riddle;
document.getElementById('solution').innerHTML = "Solution " + Solve(riddle);
<h2 id="riddle"></h2>
<h4 id="solution"></h4>
I'd like to write a function that generates the corresponding column name for a specific number, for example A for 1 or AA for 127. I know this has been answered quite a lot already, but I'd like to do it in a functional way.
Sure, you should be able to convert the existing (imperative) answers pretty easily, but I find myself getting stuck every time I try to.
Any good ideas / functional implementations?
Here's a working implementation.
var letters = ['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 toLetter(number) {
if (!number || number <= 0) {
return 0;
}
var code = "";
for (var i = 0; i < Math.floor(number / 26); i++) {
code += letters[i];
}
code += letters[(number % 26) - 1];
return code;
}
console.log(toLetter(27)); // returns AA;
I am trying to translate all alphabets in a message to p alphabet(s) in the sequence. for example: Translate("bbcd", 2) will return "ddef". I am stuck at the array part where alphabets[j+p] returns undefined. If I do alphabets[j-p], it will work.
Here is my code:
function Translate(Message, p) {
var alphabets = ['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 message_array = Message.split('');
for(var i in message_array){
for(var j in alphabets){
if(message_array[i]==alphabets[j]){
message_array[i]=alphabets[j+p];//returns undefined
}
}
}
return message_array;
}
console.log(Translate("bbcd", 2));
The problem is j+p which is doing a string concatenation, typeof j will return string as you are reading the array index using for...in
function Translate(message, p) {
var alphabets = ['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'];
return message.split('').map(function(c) {
var idx = alphabets.indexOf(c);
return idx == -1 ? c : alphabets[(idx + p) % alphabets.length]
});
}
var value = Translate("azpq", 2);
result.innerHTML = JSON.stringify(value);
<div id="result"></div>
alphabets[j+p]; returns undefined for j == 26 that it will reach for the inner loop iteration.
use indexOf method of array and change your loop structure to
for(var i in message_array)
{
var index = alphabets.indexOf( message_array[ i ] );
message_array[ i ] = alphabets[ index + 2 ];
}
This also will reach 'undefined' if your Message has a y or z in it.
so, you might want to cycle it back to the beginning
for(var i in message_array)
{
var index = alphabets.indexOf( message_array[ i ] );
message_array[ i ] = alphabets[ (index + 2) % alphabets.length ];
}
function Translate(Message, p) {
var alphabets = ['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 messageLength = Message.length;
var returnMessage = '';
for(var i=0; i< messageLength; i++) {
var myChar = Message.charAt(i);
var placeChar = alphabets.indexOf(myChar);
myChar = alphabets[placeChar + p];
returnMessage += myChar;
}
return returnMessage;
}
console.log(Translate("bbcd", 2));
You need to break once the value is found. Otherwise the inner loop will find a match again on the coming cycles.
function Translate(Message, p) {
var alphabets = ['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 message_array = Message.split('');
for (var i in message_array) {
for (var j in alphabets) {
if (message_array[i] == alphabets[j]) {
console.log(alphabets[parseInt(j) + parseInt(p)])
message_array[i] = alphabets[parseInt(j) + parseInt(p)]; //returns undefined
break;
}
}
}
return message_array;
}
alert(Translate("bbcd", 2));
You need to make sure that j+p is never bigger than the length of alphabets. This will result in a "out of bounds" error. e.g. p = 30.
A fix is to just check the length compared to p and returns something that is defined.
message_array[i] = (j+p > alphabets.length) ? alphabets[j+p] : alphabets[j]; // returns element at j if out of bounds
This suggestion works with an alphabet string instead of an array.
You can access a string like an array for a single character. For the lookup i suggest to use indexOf(), a method availabele at string objects String#indexOf() as well as array objects Array#indexOf(). Both methods returns the position of the found item/string/character or -1 if not found.
For the result I took an empty string and concatinated it with the new character.
For the correct range of the index, while adding some value, the method of choice is to use the modulo operator. It returns the reminder of a division by the second number. Here it is the length of the alphabet string.
function translate(message, p) {
var alphabet = 'abcdefghijklmnopqrstuvwxyz',
i, pos, result = '';
for (i = 0; i < message.length; i++) {
pos = alphabet.indexOf(message[i]);
result += alphabet[(pos + p) % alphabet.length];
}
return result;
}
document.write(translate("bbcd", 2));