while loop is not working properly - javascript

I am writing code for my one of the exercises in freecodecamp.com called as Pig Latin problem. For a while loop it should stop executing the code inside it when the condition becomes wrong.For my case the while loop must stop executing the codes inside it when it finds that it seen a vowel in the charas array during iteration. I think my code is correct in my point of view, but while loop is not stopping execution when it sees a vowel instead it iterate through every element in the charas array.
Here is my code
function translatePigLatin(str) {
var vowels = ['a','e','i','o','u'];
var f="";
var charas = str.split("");
if(vowels.indexOf(charas[0])!==-1) {
// vowel
charas.push("w");
charas.push("a");
charas.push("y");
f = charas.join("");
} else {
//if first letter is a consonant or cluster of consonants
var i = 0;
while(vowels.indexOf(charas[i]) ===-1) {
charas.push(charas[i]);
charas.splice(i,1);
i = i+1;
}
charas.push('a');
charas.push('y');
f = charas.join("");
}
return f;
}
translatePigLatin("california");

It works as work cause
charas.push(charas[i]);
charas.splice(i,1);
i = i+1;
After the first iteration it move 'a' letter at the begging as result i == 1 (in second iteration) charas[i] refers to consonant 'l'.

Your problem is that you increment i. You move the first element from the start of the array and then increment i you are skipping a letter because what was charas[1] is now charas[0] and what is charas[1] was charas[2].
If you step through the code and inspect the charas array you can see this happening:
Starting state:
["c", "a", "l", "i", "f", "o", "r", "n", "i", "a"]
"c" gets moved to the end
i is incremented to 1
["a", "l", "i", "f", "o", "r", "n", "i", "a", "c"]
"l" is at position 1, so it gets moved to the end.
i is incremented to 2
["a", "i", "f", "o", "r", "n", "i", "a", "c", "l"]
"f" is at position 2, so it gets moved to the end.
i is incremented to 3
["a", "i", "o", "r", "n", "i", "a", "c", "l", "f"]
"r" is at position 3, so it gets moved to the end.
i is incremented to 4
["a", "i", "o", "n", "i", "a", "c", "l", "f", "r"]
"i" is at position 4, the while condition is met, "aioniaclfr" will be returned.
If you just get rid of i and always check charas[0] it works like expected:
function translatePigLatin(str) {
var vowels = ['a','e','i','o','u'];
var f = "";
var charas = str.split("");
if(vowels.indexOf(charas[0]) !== -1) {
// vowel
charas.push("w");
charas.push("a");
charas.push("y");
f = charas.join("");
} else {
//if first letter is a consonant or cluster of consonants
while(vowels.indexOf(charas[0]) === -1) {
charas.push(charas[0]);
charas.splice(0,1);
}
charas.push('a');
charas.push('y');
f = charas.join("");
}
return f;
}
document.getElementById('out').textContent = translatePigLatin("california");
<div id="out"></div>
As a side note, this type of while condition will lead to an infinite loop if someone passes in a string that is all consonants, it will just keep shuffling the letters around because it will never find a vowel to stop it. To avoid this, I would add another if condition to check for that to make sure it won't happen:
function translatePigLatin(str) {
var vowels = ['a','e','i','o','u'];
var f = "";
var charas = str.split("");
if (!str.match(/[aeiou]+/)) {
// only consonants do something
f = str + 'ay';
} else if (vowels.indexOf(charas[0]) !== -1) {
// vowel
charas.push("w");
charas.push("a");
charas.push("y");
f = charas.join("");
} else {
//if first letter is a consonant or cluster of consonants
while(vowels.indexOf(charas[0]) === -1) {
charas.push(charas[0]);
charas.splice(0,1);
}
charas.push('a');
charas.push('y');
f = charas.join("");
}
return f;
}
document.getElementById('out').textContent = translatePigLatin("wkrp");
<div id="out"></div>
/[aeiou]+/ is a regular expression meaning any vowel one or more times anywhere in the string the ! in !str.match(/[aeiou]+/) negates the result of match, so if there are no vowels in the string that branch of the if is followed.

You haven't added the condition to exit the while loop, use i <= length of charas. Check below snippet.
function translatePigLatin(str) {
var vowels = ['a', 'e', 'i', 'o', 'u'];
var f = "";
var charas = str.split("");
if (vowels.indexOf(charas[0]) !== -1) {
// vowel
charas.push("w", "a", "y");
f = charas.join("");
} else {
//if first letter is a consonant or cluster of consonants
var i = 0;
var len = charas.length;
while (vowels.indexOf(charas[i]) === -1 && i <= len) {
charas.push(charas[i]);
charas.splice(i, 1);
i++;
}
charas.push('a', 'y');
f = charas.join("");
}
return f;
}
console.log(translatePigLatin("california"));

The problem is that your are iterating AND modifying the same array at the same time
You can simplify your code like this
function translatePigLatin(str) {
var vowels = ['a', 'e', 'i', 'o', 'u'];
//start with vowel
if (vowels.indexOf(str[0]) !== -1) {
return str + "way";
}
var i = 0;
var beforeVowel = "";
var chars = str.split("");
while (vowels.indexOf(chars[i]) === -1 && i < str.length) {
beforeVowel += chars[i];
i++;
}
return str.substring(i) + beforeVowel + "ay";
}
console.log(translatePigLatin("california"));
console.log(translatePigLatin("pig"));
console.log(translatePigLatin("eat"));

Related

Return a string where every character is replaced with its corresponding value in the cipher object

I am having an issue replacing the value from an object with the keys matching the string letters.
Challenge: Create a function secretCipher that takes in an string(sentence) and an object(cipher). Return a string where every character is replaced with its cooresponding value in the cipher. If the character doesn't exist in the cipher, use the original character.
This is what I've written so far. My console.log's are showing what I need them to however my hang up appears to be somewhere when I try to identy the same corresponding keys to the sentence[i]. I have tried numerous methods like .includes, strictly equals etc.
I also have tried to use .replace to replace any sentence[i] with by value variable.
function secretCipher(sentence, cipher){
let result = '';
let cyf = Object.keys(cipher)
// console.log(cyf)
for (let key in cipher){
let value = cipher[key];
// console.log(value)
}
// iterate through sentence, if char in string is stricktly equal to the key
for (let i = 0; i < sentence.length; i++) {
// console.log(sentence[i])
if (sentence[i].includes(cyf)){
sentence[i] = sentence[i].replace(sentence[i], value)
result += sentence[i]
} else {
result += sentence[i]
}
}
return result;
}
//Uncomment the lines below to test your function:
console.log(secretCipher("lqq me on flcebzzk" , { l : "a", q : "d", z: "o"})); //=> "add me on facebook"
console.log(secretCipher("where are you???" , { v : "l", '?' : "!"})) //=> "where are you!!!"
console.log(secretCipher("twmce" , { m : "n", t : "d", w : "a"})); //=> "dance"
You can iterate through the word more efficiently, but this is pretty simple:
function secretCipher(sentence, cipher){
return sentence.split('').map(letter => cipher[letter] ?? letter).join('');
}
//Uncomment the lines below to test your function:
console.log(secretCipher("lqq me on flcebzzk" , { l : "a", q : "d", z: "o"})); //=> "add me on facebook"
console.log(secretCipher("where are you???" , { v : "l", '?' : "!"})) //=> "where are you!!!"
console.log(secretCipher("twmce" , { m : "n", t : "d", w : "a"})); //=> "dance"
If your browser does not know what ?? is, replace it with ||. It should work on all modern browsers according to MDN.
let value = cipher[key]; does nothing because that value isn't referenced anywhere else (its scope ends after the loop iteration ends)
if (sentence[i].includes(cyf)) { doesn't make sense because sentence[i] is a string (character) and cyf is an array of keys. A string will not contain an array. Check if the array contains the character instead.
sentence[i] = sentence[i].replace(sentence[i], value) won't work because strings are immutable. Create a new string instead.
When a match is found, adding the original sentence[i] doesn't make sense - you want to replace with the value on the object, not with the original character.
function secretCipher(sentence, cipher) {
let result = '';
const keys = Object.keys(cipher);
// iterate through sentence, if char in string is stricktly equal to the key
for (let i = 0; i < sentence.length; i++) {
if (keys.includes(sentence[i])) {
result += cipher[sentence[i]];
} else {
result += sentence[i]
}
}
return result;
}
console.log(secretCipher("lqq me on flcebzzk", {
l: "a",
q: "d",
z: "o"
})); //=> "add me on facebook"
console.log(secretCipher("where are you???", {
v: "l",
'?': "!"
})) //=> "where are you!!!"
console.log(secretCipher("twmce", {
m: "n",
t: "d",
w: "a"
})); //=> "dance"
or, more concisely
function secretCipher(sentence, cipher) {
let result = '';
for (const char of sentence) {
result += cipher[char] ?? char;
}
return result;
}
console.log(secretCipher("lqq me on flcebzzk", {
l: "a",
q: "d",
z: "o"
})); //=> "add me on facebook"
console.log(secretCipher("where are you???", {
v: "l",
'?': "!"
})) //=> "where are you!!!"
console.log(secretCipher("twmce", {
m: "n",
t: "d",
w: "a"
})); //=> "dance"

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"))

Checking values of one array against another in JS

I'm trying to do a check that the first array contains the same values of the second array.
However I'm confused about my code.
First question is: why is my code running my else statement if all letters in the first array are contained in the second? it will run 2 lines of "this is not valid"
Second question is: if my first array contains a duplicate letter it will still pass the check e.g
["a", "b" , "a", "d", "e", "f"]; even though there is two a's in the first it will see the same "a" again. Anyone know a way around this.
Sorry for my long winded questions but I hope it makes sense. Thanks :)
var letters = ["a", "b" , "c", "d", "e", "f"];
var otherLetters = ["a","b", "c" , "d", "e", "f"];
var i = -1;
while(i<=letters.length){
i++;
if(otherLetters.includes(letters[i])){
console.log("This is valid");
}
else
console.log("This is not valid");
}
You didn't close the brackets. And your loop is very confusing, please use foreach. Here is a working example:
const letters = ["a", "b" , "c", "d", "e", "f"];
const otherLetters = ["a","b", "c" , "d", "e", "f"];
letters.forEach(el => {
if (otherLetters.includes(el)) {
console.log(el + 'is valid');
} else {
console.log(el + 'is not valid');
}
});
You are trying to access array elements which are out of bounds. The script runs 8 iterations over an array with 6 elements.
Nothing to worry, cpog90.
Try this solution.
var letters = ["a", "b" , "c", "d", "e", "f"];
var otherLetters = ["a","b", "c" , "d", "e", "f"];
var i = 0;
while(i<letters.length){
if(otherLetters.includes(letters[i])){
console.log("This is valid");
}
else {
console.log("This is not valid "+i);
}
i++;
}
What went wrong in your logic?
If you declare i = -1 and while(i<=letters.length), As 6 is length of letters, 8 iterations will be done as follows.
For first iteration (i = -1), 'while' condition returns true and checks for 'a'
output: This is valid
For second iteration (i = 0), 'while' condition returns true and checks for 'b'
output: This is valid
For third iteration (i = 1), 'while' condition returns true and checks for 'c'
output: This is valid
For fourth iteration (i = 2), 'while' condition returns true and checks for 'd'
output: This is valid
For fifth iteration (i = 3), 'while' condition returns true and checks for 'e'
output: This is valid
For sixth iteration (i = 4), 'while' condition returns true and checks for 'f'
output: This is valid
For seventh iteration (i = 5), 'while' condition returns true and checks for undefined value.
output: This is not valid
For eighth iteration (i = 6), 'while' condition returns true and checks for undefined value.
output: This is not valid
First of all you have set i = -1 which is confusing since array start position is 0.
The reason your loop is running two extra times is because loop started at -1 instead of 0 and next the condition i <= length.
Since [array length = last index + 1] your loop runs extra two times.
Just to make your code work assign var i = 0 and while condition i < letters.length
Simplest solution is using lodash. It has all optimizations out-of-the-box:
var letters = ["a", "b" , "c", "d", "e", "f"];
var otherLetters = ["f", "a","b", "c" , "d", "e"];
const finalLetters = _.sortBy(letters);
const finalOtherLetters = _.sortBy(otherLetters);
if (_.isEqual(finalLetters, finalOtherLetters)) {
console.log('Two arrays are equal.');
} else {
console.log('Two arrays are not equal.');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Arrays are index based and starts from 0. So, the -1 and the less than letters.length check puts the code into out of bounds.
var letters = ["a", "b" , "c", "d", "e", "f"];
var otherLetters = ["a","b", "c" , "d", "e", "f"];
var i = 0;
while(i<letters.length)
{
if(otherLetters.includes(letters[i]))
{
console.log("This is valid");
}
else
{
console.log("This is not valid");
}
i++;
}
You can use a combination of Array.prototype.every with Array.prototype.includes, along with some extra guard clauses.
const areSequenceEqual = (arr1, arr2) => {
if (!arr1 || !arr2) {
return false;
}
if (arr1.length !== arr2.length) {
return false;
}
return arr1.every(x => arr2.includes(x));
};
const letters = ["a", "b", "c", "d", "e", "f"];
const otherLetters = ["a", "b", "c", "d", "e", "f"];
const someOtherLetters = ["a", "b", "c", "d", "e", "f", "g"];
console.log(areSequenceEqual(letters, otherLetters));
console.log(areSequenceEqual(letters, undefined));
console.log(areSequenceEqual(letters, someOtherLetters));

Displaying strings based upon certain alphabet letters

I am trying to understand why my code isn't working. I'm creating a function that takes in a letter of the alphabet. If the letter is a, e, i, o or u then my program should display a message indicating that the entered letter is a vowel. If the letter is y then my function should display a message indicating that sometimes y is a vowel, and sometimes y is a consonant. Otherwise my function should display a message indicating that the letter is a consonant.
Here's my code
function vowelOrCons(letter) {
if((letter) === 'a'; 'e'; 'i'; 'o'; 'u') {
return 'vowel';
} else if {
((letter) === 'y') {
return 'vowel or consonant';
} else if {
((letter) === "b", "c", "d", "f", "g", "h", "j", "k", "l",
"m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "z")
return 'consonant';
}
else
{
return 'Your entry is invalid';
}
}
}
I'm trying to learn Javascript better so I appreciate any help, resources, and/or explanations regarding my work, thanks!
You could use String#includes and return if true with the message.
This code features the early exit approach by taking an if clause and return. The advantage is to omit else parts because the return exits the function.
function vowelOrCons(letter) {
letter = letter.toLowerCase();
if ('aeiou'.includes(letter)) {
return 'vowel';
}
if (letter === 'y') {
return 'vowel or consonant';
}
if ('bcdfghjklmnpqrstvwxz'.includes(letter)) {
return 'consonant';
}
return 'Your entry is invalid';
}
console.log(vowelOrCons('a'));
console.log(vowelOrCons('b'));
console.log(vowelOrCons('1'));
You problem is syntax.
if ((condition || another-condition) && condition) {
}
else if (some-other-condition) {
}
else {
}
Now concerning the checking, it can be shorten by using includes string method.
const firstLetter = letter.charAt(0);
if ("aeiou".includes(firstLetter)) { .. }
Ok this need a bit of work.
Take a look at the fixed code below.
First off we move the test letters to arrays and used index of.
Then we fixed syntax issues and added test log at end.
function vowelOrCons(letter) {
let vowels = ['a', 'e', 'i', 'o', 'u'];
let consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l",
"m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
if(vowels.indexOf(letter) > -1 ) {
return 'vowel';
} else if(letter === 'y') {
return 'vowel or consonant';
} else if(consonants.indexOf(letter) > -1 ){
return 'consonant';
} else {
return 'Your entry is invalid';
}
}
console.log(vowelOrCons('a'));
console.log(vowelOrCons(''));
console.log(vowelOrCons('1'));
console.log(vowelOrCons('y'));
console.log(vowelOrCons('w'));
I would suggest you declare an array for vowels, one for consonants and then check if the letter intered is in the array but first check if it is a 'y' as shown below;
var vowels = ['a', 'e', 'i',....]];
var consonants = ['b', 'c', 'd', ....]];
if ('y' == letter) {
// print your sentence
}
else if (/* letter is in the vowels array */) {
// print your sentence
}
else if (/* letter is in the consonants array */) {
// print your sentence
}
Good luck

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