Javascript Not Executing First and Last Loop - javascript

Working on a CoderByte problem and the first for loop isn't executing on the first or last loop. The function is to convert every letter to the next one after it in the alphabet, then switch all vowels to uppercase. For example input "coderbyte" is returning "fpdfsczUE" when it should be "dpdfsczUf". All variables and tests seem to be functioning (see comments). Any help would be appreciated - they give the answers but won't explain why this won't work.
function LetterChanges(str) {
// convert every letter in a string to the letter after in the alphabet,
// then convert all vowels to uppercase, and return the string.
var alpha = "abcdefghijklmnopqrstuvwxyz";
var vowels = "aeiou";
for (var i=0; i<str.length; i++) {
// return (alpha.indexOf(str[i]) !== -1); checks true on first loop
if (alpha.indexOf(str[i]) !== -1) {
//return alpha[alpha.indexOf(str[i]) + 1]; // returns d correctly
// return alpha.indexOf(str[i]) + 1; // returns 3 correctly
//return str[i]; returns "c" correctly
// return str.replace(str[i], "X"); returns Xoderbyte correctly
// return str.replace(str[i+1], alpha[alpha.indexOf(str[i]) + 1]);
//returns cdderbyte correctly
// BUG: why isn't the line below working on the first and last
// loop ?
// ie. input "coderbyte" returns "fpdfsczUE"
str = str.replace(str[i], alpha[alpha.indexOf(str[i]) + 1]);
}
}
//return alpha[alpha.indexOf(str[i])];
for (var j=0; j<str.length; j++) {
if (vowels.indexOf(str[j]) !== -1) {
str = str.replace(str[j], str[j].toUpperCase());
}
}
return str;
}

Your question output is not proper
"coderbyte" needs to change to "dpEfsczUf" not "dpdfsczUf".
We need to replace character at particular index as str.replace will replace first occurrence only.
replaceAt function can help
So modified code below can work
String.prototype.replaceAt=function(index, character) {
return this.substr(0, index) + character + this.substr(index+character.length);
}
function LetterChanges(str) {
// convert every letter in a string to the letter after in the alphabet,
// then convert all vowels to uppercase, and return the string.
var alpha = "abcdefghijklmnopqrstuvwxyz";
var vowels = "aeiou";
for (var i=0; i<str.length; i++) {
if (alpha.indexOf(str[i]) !== -1) {
str = str.replaceAt(i, alpha[alpha.indexOf(str[i]) + 1]);
}
}
for (var j=0; j<str.length; j++) {
if (vowels.indexOf(str[j]) !== -1) {
str = str.replaceAt(j, str[j].toUpperCase());
}
}
return str;
}

You're misunderstanding how .replace() works. (see Satyajit's comment on your question) This is why the second part of your function works flawlessly; once a vowel has been converted to upper case, it can't be the basis of another character's transformation.
Regarding the first loop: if a letter in a pass after the first one matches a letter closest to i=0, then that letter will be the one that's replaced, not the one you intended.
If you use a debugger or even add a console.log(str) after the first loop's assignment, it will be evident what's going on.

Related

How to get odd and even position characters from a string?

I'm trying to figure out how to remove every second character (starting from the first one) from a string in Javascript.
For example, the string "This is a test!" should become "hsi etTi sats!"
I also want to save every deleted character into another array.
I have tried using replace method and splice method, but wasn't able to get them to work properly. Mostly because replace only replaces the first character.
function encrypt(text, n) {
if (text === "NULL") return n;
if (n <= 0) return text;
var encArr = [];
var newString = text.split("");
var j = 0;
for (var i = 0; i < text.length; i += 2) {
encArr[j++] = text[i];
newString.splice(i, 1); // this line doesn't work properly
}
}
You could reduce the characters of the string and group them to separate arrays using the % operator. Use destructuring to get the 2D array returned to separate variables
let str = "This is a test!";
const [even, odd] = [...str].reduce((r,char,i) => (r[i%2].push(char), r), [[],[]])
console.log(odd.join(''))
console.log(even.join(''))
Using a for loop:
let str = "This is a test!",
odd = [],
even = [];
for (var i = 0; i < str.length; i++) {
i % 2 === 0
? even.push(str[i])
: odd.push(str[i])
}
console.log(odd.join(''))
console.log(even.join(''))
It would probably be easier to use a regular expression and .replace: capture two characters in separate capturing groups, add the first character to a string, and replace with the second character. Then, you'll have first half of the output you need in one string, and the second in another: just concatenate them together and return:
function encrypt(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
console.log(encrypt('This is a test!'));
Pretty simple with .reduce() to create the two arrays you seem to want.
function encrypt(text) {
return text.split("")
.reduce(({odd, even}, c, i) =>
i % 2 ? {odd: [...odd, c], even} : {odd, even: [...even, c]}
, {odd: [], even: []})
}
console.log(encrypt("This is a test!"));
They can be converted to strings by using .join("") if you desire.
I think you were on the right track. What you missed is replace is using either a string or RegExp.
The replace() method returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match. If pattern is a string, only the first occurrence will be replaced.
Source: String.prototype.replace()
If you are replacing a value (and not a regular expression), only the first instance of the value will be replaced. To replace all occurrences of a specified value, use the global (g) modifier
Source: JavaScript String replace() Method
So my suggestion would be to continue still with replace and pass the right RegExp to the function, I guess you can figure out from this example - this removes every second occurrence for char 't':
let count = 0;
let testString = 'test test test test';
console.log('original', testString);
// global modifier in RegExp
let result = testString.replace(/t/g, function (match) {
count++;
return (count % 2 === 0) ? '' : match;
});
console.log('removed', result);
like this?
var text = "This is a test!"
var result = ""
var rest = ""
for(var i = 0; i < text.length; i++){
if( (i%2) != 0 ){
result += text[i]
} else{
rest += text[i]
}
}
console.log(result+rest)
Maybe with split, filter and join:
const remaining = myString.split('').filter((char, i) => i % 2 !== 0).join('');
const deleted = myString.split('').filter((char, i) => i % 2 === 0).join('');
You could take an array and splice and push each second item to the end of the array.
function encrypt(string) {
var array = [...string],
i = 0,
l = array.length >> 1;
while (i <= l) array.push(array.splice(i++, 1)[0]);
return array.join('');
}
console.log(encrypt("This is a test!"));
function encrypt(text) {
text = text.split("");
var removed = []
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed.push(letter)
return false;
}
return true
}).join("")
return {
full: encrypted + removed.join(""),
encrypted: encrypted,
removed: removed
}
}
console.log(encrypt("This is a test!"))
Splice does not work, because if you remove an element from an array in for loop indexes most probably will be wrong when removing another element.
I don't know how much you care about performance, but using regex is not very efficient.
Simple test for quite a long string shows that using filter function is on average about 3 times faster, which can make quite a difference when performed on very long strings or on many, many shorts ones.
function test(func, n){
var text = "";
for(var i = 0; i < n; ++i){
text += "a";
}
var start = new Date().getTime();
func(text);
var end = new Date().getTime();
var time = (end-start) / 1000.0;
console.log(func.name, " took ", time, " seconds")
return time;
}
function encryptREGEX(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
function encrypt(text) {
text = text.split("");
var removed = "";
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed += letter;
return false;
}
return true
}).join("")
return encrypted + removed
}
var timeREGEX = test(encryptREGEX, 10000000);
var timeFilter = test(encrypt, 10000000);
console.log("Using filter is faster ", timeREGEX/timeFilter, " times")
Using actually an array for storing removed letters and then joining them is much more efficient, than using a string and concatenating letters to it.
I changed an array to string in filter solution to make it the same like in regex solution, so they are more comparable.

Why isn't this function removing the vowels?

I created a function that takes a string converts the string to an array and then compares each string character to each vowel in an array, removing the string character if it matches one. It doesnt seems to be working correctly on longer words. For example with "tom" the o would be removed but with "johnson" only the first o is removed and then the n is removed at the end as well. I'm not seeing the issue.
function removeVolwels(string){
let strAr= function(string){
//converts to to lower case and turns string into an array
let s= string.toLowerCase();
let strAr= s.split("");
return strAr;
}(string);
//stores vowels
let vowels=["a","e","o","i","u","y"];
//loops through each character
for(let i=0; i < string.length -1; i++){
console.log("i index " + i);
//compares each character in the string to a every vowel until it matches one
for(let j=0; j < vowels.length; j++){
console.log("j index " + j + " and " +vowels[j]);
if(string[i] === vowels[j]){
console.log(string[i].toString() + " === "+vowels[j]);
console.log("removed " + string[i]);
//removes vowel if letter matches one
strAr.splice(i,1);
console.log(strAr)
}
}
}
return strAr.join("");
}
console.log('tom => ' + removeVolwels('tom'));
console.log('johnson => ' + removeVolwels('johnson'));
The problem is that you call strAr.splice(i,1);.
So, you have word "johnson", and after removing first "o" you've got "jhnson", but current length of string is 6 instead of 7 in the beginning!
When you get to next "o" - i have value of 5 (which is correct for "johnson" string but not "jhnson").
Also, there is another bug in loop - you have condition i < string.length -1. That means you will never reach last character. It should be i < string.length.
So, if you want to reuse your solution you can write something like this:
function removeVolwels(string){
let strAr= function(string){
//converts to to lower case and turns string into an array
let s= string.toLowerCase();
let strAr= s.split("");
return strAr;
}(string);
//stores vowels
let vowels=["a","e","o","i","u","y"];
let returnVal = [];
//loops through each character
for(let i=0; i < string.length; i++){
console.log("i index " + i);
// simple flag to match if letter should be added to return array
let shouldBeAdded = true;
//compares each character in the string to a every vowel until it matches one
for(let j=0; j < vowels.length; j++){
console.log("j index " + j + " and " +vowels[j]);
if(string[i] === vowels[j]){
// when it is some of the vowels it should not be added, so we change the flag, and break 'vowel loop'
shouldBeAdded = false;
break;
}
}
// if flag is true then add letter to result array
if(shouldBeAdded === true) {
returnVal.push(string[i])
}
}
return returnVal.join("");
}
console.log('tom => ' + removeVolwels('tom'));
console.log('johnson => ' + removeVolwels('johnson'));
You seem to be overcomplicating things a bit. Below is a much more streamlined way of doing what you're after (code commented).
function removeVowels(string) {
// convert string to lowercase and split into array 's'
let s = string.toLowerCase().split("");
// define our list of vowels
let vowels = ["a", "e", "o", "i", "u", "y"];
// loop over array 's' in reverse. if the letter we're iterating over is in the vowels array, remove it. We do this in reverse because we'd skip letters if we went from front to back due to the splice.
for (let i = s.length-1; i >= 0; i--) {
if (vowels.indexOf(s[i]) > -1) {
s.splice(i, 1); // 'i' is the index to start at (which we get from our loop) and 1 is the number of characters to remove.
}
}
return s.join("");
}
console.log('tom => ' + removeVowels('tom'));
console.log('johnson => ' + removeVowels('johnson'));
console.log('aoeieyyozoyyeieoa => ' + removeVowels('aoeieyyozoyyeieoa'));
Because after executing,
strAr.splice(i,1)
index in original string and index in strAr are not same for the same character.
So, you need to twist your logic for that.
Here's a simpler approach that utilizes Array.prototype.filter,Array.prototype.join, and Array.prototype.indexOf methods. The following code also uses String.prototype.split method to convert a string into a character array:
//1st param is the string you want to remove letters from
//2nd param is an array containing the letters to remove
function removeLetters(str, toRemove) {
//Create an array - each index contains a single character
let letters = str.split('');
//Use Array.prototype.filter method to remove any letter that occurs in "toRemove" array
let filtered = letters.filter(letter => toRemove.indexOf(letter) === -1)
//Turn the filtered array back into a string
return filtered.join('');
}
//The letters we want to remove are all vowels, so:
const vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
//Some test cases
const word = "tommy";
const word2 = "johnson";
const word3 = "aeioux";
console.log(removeLetters(word, vowels));
console.log(removeLetters(word2, vowels));
console.log(removeLetters(word3, vowels));

How to make element in Array change its' place

I'm beginner in JS. I've tried to understand Caesar Cipher ROT13, but it was too complicated for me. So I've tried to write my own code. Here it is below:
function encrip() {
var 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"];
var str = "Ni Hao";
var string = str.toUpperCase();
for (var i = 0; i < string.length; i++) {
for (var k = 0; k < alphabet.length; k++) {
if(string.charAt(i) == alphabet[k]) {
/* console.log(string.charAt(i) + ' ' + alphabet.indexOf(alphabet[k])); */
}
}
}
}
encrip();
But I am stuck. How to do:
1. Get value from var str and then access to var alphabet , after change each letter from var str value to next 3 from alphabet (var str each element's current position would be changed) For example: Input: Ni Hao ==> output: QL KDR
2. Create universal code, I mean, not only for changing position by 3, but when I give value '5', each element would be changed by next 5 positions from alphabet. So output can be changed when I change its' value
I hope I explained everything clearly. Thanks everyone in advance for help!!
you can use the following function to encrypt english words, the 1st parameter is the string to encrypt and the 2nd for shifting
function encryp(str,pos){
var alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var strUC=str.toUpperCase();
var enc="";
for(var i=0;i<strUC.length;i++){
if(strUC.charAt(i)!=" "){
enc+=alpha.charAt((alpha.indexOf(strUC.charAt(i))+pos)%26)
}
else{
enc+=" "
}
// in your case pos=3
}
return enc;
}
console.log(encryp("NiHao",3));
You don't need two for loops to do this. Iterate over the input string and find the index of each character in the alphabet array, if found add the shift to it to get the encrypted character.
To handle overflow use the modulus operator to cycle through the array.
Also I assume that you are not going use any special symbols to do the encryption.
function encrip(string, shift) {
var 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"];
string = string.toUpperCase();
let arr = [];
for (var i = 0; i < string.length; i++) {
let char = alphabet.indexOf(string[i]) !== -1 ? alphabet[(alphabet.indexOf(string[i]) %26) + shift] : " ";
arr.push(char);
}
let encryp = arr.join("");
console.log(encryp);
return encryp;
}
encrip("Ni Hao", 3);
First of all, instead of your inner for loop scanning the whole alphabet array, you can use the built-in function indexOf:
alphabet.indexOf('K') // returns 10
Secondly, you'll want to build up your enciphered string in a separate variable. For each letter, get the index of that letter in the alphabet, add your cipher offset parameter to that index and add the resulting letter from the alphabet to your new string. An important step is that when you add to the index of the letter, you want to make sure the resulting index is within range for the alphabet array. You can do that using the % (modulo) operator, which will wrap high values back round to the start of the array. In full:
function encipher(input, offset) {
var 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"];
var str = input.toUpperCase();
var result = '';
for (var i = 0; i < str.length; i++) {
letterIndex = alphabet.indexOf(str.charAt(i));
if (letterIndex === -1) {
result += str[i]; // if the letter isn't found in the alphabet, add it to the result unchanged
continue;
}
cipheredIndex = (letterIndex + offset) % alphabet.length; // wrap index to length of alphabet
result += alphabet[cipheredIndex];
}
console.log(result);
}
encipher('Ni Hao', 5); // output: 'SN MFT'

Comparing a reversed string to it's original without using .reverse() method

I'm attempting to compare an original string argument with its reverse. In essence, this function is supposed to verify whether or not a given string is a palindrome. Key points:
String needs to be converted to all lowercase, which I did.
String needs to consist of only alphanumeric characters, which I did.
When compared, the original string and formatted string must match. If they do, the boolean value of true gets returned, otherwise false does.
Here is the source code: JS Fiddle | Alternatively, code is below:
function palindrome(str) {
var reverseString;
var temp;
var formatted;
// make sure input gets converted to lowercase
temp = str.toLowerCase();
// make sure all non-alphanumeric characters get removed
// once converted to lowercase, ensure that all special characters and digits are stripped from the string
formatted = temp.replace(/[^A-Za-z]/g, '');
// now we need to compare two strings: the raw input vs the string in reverse
for (i = formatted.length -1; i >= 0; i--) {
reverseString += formatted[i];
}
if (reverseString === str) {
return true;
}
return false;
}
palindrome("123$EYE");
function palindrome(str) {
var reverseString=""; // initialize every string to ""
var temp="";
var formatted="";
temp = str.toLowerCase();
formatted = temp.replace(/[^A-Za-z0-9]/g, ''); // I added 0-9 in your regex to have numbers in your string
for (i = formatted.length -1; i >= 0; i--) {
reverseString += formatted[i];
}
if (reverseString === formatted) { // change str to formatted
return true;
}
return false;
}
var isPal = palindrome("123$EYE");
alert(isPal); // try it on `alert` if it is true or false
Your code is ok. But you have some flaws. You should initialize your String to "" so it will not have a value of undefined. The one you put on your if statement is str which is your original String word, you should put your formatted String because that is the one you removed the special characters.
Why reverse and compare? You just need to compare the characters of the same position from the head and from the tail. First str[0] and str[length - 1], then str[1] and str[length - 2], and so on. Till you reach the middle, or any comparison fails.
function isPalindrome(str) {
var len = str.length
for (var i = 0; i < Math.ceil(len/2); i++) {
if (str[i] !== str[len - 1 - i]) {
// or add more comparison rules here
return false
}
}
return true
}
isPalindrome('1') // true
isPalindrome('121') // true
isPalindrome('1221') // true
isPalindrome('1211') // false
alphanumeric characters?
function palindrome(str) {
var temp;
temp = str.toLowerCase();
temp = temp.replace(/[^A-Za-z0-9]/g, '');
console.log(temp);
for (let a = 0, b = temp.length - 1; b > a; a++, b--) {
if (temp.charAt(a) !== temp.charAt(b))
return false;
}
return true;
}
You didn't initialize reverseString so it will be undefined at the beginning. Adding a character 'a' to undefined returns a string 'undefineda', instead of 'a' which you probably expect.
Change your code to
var reverseString = '';
and it'll work
Here is an elegant way to reverse the text:
var rev = temp.split('').reverse().join(''); // reverse
check out fiddle:
function palindrome(str)
{
var temp = str.toLowerCase() // converted to lowercase
.replace(/[^A-Za-z]/g, ''); // non-alphanumeric characters removed
var rev = temp.split('').reverse().join(''); // reverse
console.log(temp, ' === ', rev, ' is ', temp === rev);
if(temp === rev)
{
return true;
}
return false;
}
var out = palindrome("123$EYE");
document.getElementById('divOutput').innerHTML = out;
<div id="divOutput"></div>

put dash after every n character during input from keyboard

$('.creditCardText').keyup(function() {
var foo = $(this).val().split("-").join(""); // remove hyphens
if (foo.length > 0) {
foo = foo.match(new RegExp('.{1,4}', 'g')).join("-");
}
$(this).val(foo);
});
I found this tutorial on putting dash after every 4 character from here my question is what if the character interval is not constant like in this example it is only after every 4 what if the interval is 3 characters "-" 2 characters "-" 4 characters "-" 3 characters "-" so it would appear like this 123-12-1234-123-123.
In this case, it is more convenient to just write normal code to solve the problem:
function format(input, format, sep) {
var output = "";
var idx = 0;
for (var i = 0; i < format.length && idx < input.length; i++) {
output += input.substr(idx, format[i]);
if (idx + format[i] < input.length) output += sep;
idx += format[i];
}
output += input.substr(idx);
return output;
}
Sample usage:
function format(input, format, sep) {
var output = "";
var idx = 0;
for (var i = 0; i < format.length && idx < input.length; i++) {
output += input.substr(idx, format[i]);
if (idx + format[i] < input.length) output += sep;
idx += format[i];
}
output += input.substr(idx);
return output;
}
$('.creditCardText').keyup(function() {
var foo = $(this).val().replace(/-/g, ""); // remove hyphens
// You may want to remove all non-digits here
// var foo = $(this).val().replace(/\D/g, "");
if (foo.length > 0) {
foo = format(foo, [3, 2, 4, 3, 3], "-");
}
$(this).val(foo);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input class="creditCardText" />
While it is possible to do partial matching and capturing with regex, the replacement has to be done with a replacement function. In the replacment function, we need to determine how many capturing group actually captures some text. Since there is no clean solution with regex, I write a more general function as shown above.
You can split it using a regular expression. In this case, I'm using a expression to check for non-spaces with interval 3-2-4-3.
The RegExp.exec will return with a "match" array, with the first element containing the actual string. After removing the first element of the match, you can then join them up with dashes.
var mystring = "123121234123"
var myRegexp = /^([^\s]{3})([^\s]{2})([^\s]{4})([^\s]{3})$/g
var match = myRegexp.exec(mystring);
if (match)
{
match.shift();
mystring = match.join("-")
console.log(mystring)
}
Per further comments, the op clarified they need a fixed interval for when to insert dashes. In that case, there are several ways to implement it; I think regular expression would probably be the worst, in other words, overkill and overly complication solution.
Some simpler options would be to create a new character array, and in a loop append character by character, adding a dash too every time you get to the index you want. This would probably be the easiest to write and grok after the fact, but a little more verbose.
Or you could convert to a character array and use an 'insert into array at index'-type function like splice() (see Insert Item into Array at a Specific Index or Inserting string at position x of another string for some examples).
Pass the input value and the indexes to append the separator, first, it will remove the existing separators then just append separators on positions indexes.
export function addSeparators(
input: string,
positions: number[],
separator: string
): string {
const inputValue = input.replace(/-/g, '').split(''); // remove existing separators and split characters into array
for (let i = 0; i < inputValue.length; i++) {
if (positions.includes(i)) inputValue.splice(i, 0, separator);
}
return inputValue.join('');
}

Categories

Resources