Why isn't this function removing the vowels? - javascript

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

Related

Implement the .split method

I need to implement the .split method in my own way without using prebuilt functions. The method should receive a string divided into 2 sentences by a dot and divide them through a separator.
For example, there is this string:
'word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', separator in this case: '. '
The result should be:
['word wooord wooooooooord wooooooord", "wooooooooord woooooord woooord wooooooooord']
I tried to implement it myself, the first problem I encountered is that the words from the string are added character by character to the new array. The second problem is that the output is still a string even though I declared an array earlier.
function split(str, splitter){
let arrSent = []
for (let i = 0; i < str.length; i++){
if (str[i] != splitter){
arrSent += str[i]
}
}
return arrSent
}
console.log(split('word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', '. '))
Since the delimiter can have more than one character, you need a system to upfront collect a sample of characters (of the same length as the delimiter) to be then compared with the delimiter:
const split = (str, delimiter) => {
// If delimiter is empty string just return an array of characters
if (delimiter === "") return [...str];
const len = delimiter.length;
const iter = str.length - len + 1; // max needed iterations
const arr = [""]; // Prefill it with empty string
let idx = 0; // arr insertion pointer
for (let i = 0; i < iter; i++) {
// Collect len chars from str as a sample
// to compare with the delimiter:
let sample = "";
for (let x = i; x < i + len; x++) {
sample += str[x];
}
const isSplit = sample === delimiter;
const isEnded = i === iter - 1;
if (isSplit) {
i += len - 1; // Consume splitted characters
idx += 1; // Increment arr pointer
arr[idx] = ""; // Prepare the new array key as empty string
} else {
// If loop ended append the entire sample.
// Otherwise, append a single character:
arr[idx] += isEnded ? sample : str[i];
}
}
return arr
}
console.log(split("word. etc", ". "));
console.log(split("word. etc. ", ". "));
console.log(split(". word yep. . etc. ", ". "));
console.log(split("word", ". "));
console.log(split("word", "word"));
console.log(split("word", ""));
console.log(split("", ""));
above, idx (starting at 0) is used as the output's arr insertion pointer. The idx is incremented if the sample matches the delimiter. Also, if there's a match, we need to skip iterations i += len, to not include the delimiter in the output array.
To test, create many examples and right before return arr; use console.log(JSON.stringify(arr) === JSON.stringify(str.split(delimiter))); - it should return true for all the submitted tests.

Insert Spaces into string at an index

I'm trying to do this Codewars problem.
Task
In this simple Kata your task is to create a function that turns a string into a Mexican Wave. You will be passed a string and you must return that string in an array where an uppercase letter is a person standing up.
Rules
The input string will always be lower case but maybe empty.
If the character in the string is whitespace then pass over it as if it was an empty seat.
Example
wave("hello") => ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
My code so far is hosted on this repl.it
My thought process is as follows:
Turn argument into array
manipulate each index of the array at index and then readjust previous index to make a wave pattern
turn array into string
reinsert spaces before logging it to console and restarting the loop
I'm pretty stuck and my mind is stuck on how to use
for(var j = 0; j < indexSpaceNumber.length; j++){
//join and add in the spaces at their former index before returning string
strToArray[indexSpaceNumber[j]].slice(0, " ");
}
to insert the spaces into the string.
If there's any guidance or tips it would be much appreciated. I feel like I'm close, but so frustratingly far.
The main idea would be:
Iterate the characters
Replace the character in the original string with an uppercase version
You can use Array.from() to convert the string to an array, and map each item to a new string. If the character is a space return something falsy (en empty string in the example). After the creating the array, filter all falsy values:
const wave = str =>
Array.from(str, (c,i) => // convert the string to an array
// replace the character with an uppercase version in the original string
c === ' ' ?
''
:
`${str.substring(0, i)}${c.toUpperCase()}${str.substring(i + 1)}`
).filter(c => c)
const result = wave("hello")
console.log(result)
For string with spaces
function wave(str) {
let res = []
str.toLowerCase().split('').forEach((v, i) => {
if(v == ' ') return;
res.push( str.substr(0, i) + v.toUpperCase() + str.substr(i + 1) )
});
return res
}
console.log(wave("hello hello"))
I'd go recursive ;)
You know that for a string of length n you need an array of the same length. That's your exit condition.
You can use the length of the array at each iteration to work out the shape of the next string:
hello [] [Hello] 0: uppercase 1st char and append
hello [Hello] [Hello hEllo] 1: uppercase 2nd char and append
hello [Hello hEllo] [Hello hEllo heLlo] 2: uppercase 3rd char and append
...
const wave =
(str, arr = []) =>
str.length === arr.length
? arr
: wave
( str
, [ ...arr
, str.slice(0, arr.length)
+ str[arr.length].toUpperCase()
+ str.slice(arr.length + 1)
]
);
console.log(wave('hello'));
Go over each char in string and build
Slice str from start till current char + current char to upper case + Slice str from current char to end
const wave = str => {
const res = [];
for (let i = 0; i < str.length; i++) {
res.push(`${str.slice(0, i)}${str[i].toUpperCase()}${str.slice(i + 1)}}`);
}
return res;
};
console.log(wave("hi my name is rylan"));
// Alternate way to do with Array.splice
const wave2 = str => {
const res = [];
for (let i in str) {
const temp = Array.from(str);
temp.splice(i, 1, temp[i].toUpperCase());
res.push(temp)
}
return res.map(x => x.join(''));
};
console.log(wave2("hi my name is rylan"));

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.

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'

Javascript Not Executing First and Last Loop

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.

Categories

Resources