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"));
Related
I need to iterate over an input string and return a string with the count of the frequency of the different vowels in the string. The vowels in the return string should be in the order they appear in the input string. So, if 'hello world' is the function's parameter, the function should return e1o2. The code I have so far is below. It returns e1o2o3. For some reason, it is not stopping the count of o after it hits the o in hello, and seems to be counting the o in world as a separate count. I think it is, finalString += char + sum;, that is causing this. But, I do not know how to create this function in the first place. Thank you for your time.
function vowelFrequency(str) {
let finalString = '';
let sum = 0;
for (let char of str) {
if ('aeiou'.includes(char)) {
sum += 1;
finalString += char + sum;
}
}
return finalString;
};
The main problem is your sum counter. It counts all vowels together.
Better appoach would be to create a dictionary of vowels
where we add +1 every time we met a match.
In short the idea is:
if (char === 'e') {
dic['e'] += 1;
}
const text = 'hello world';
function vowelFrequency(str) {
let finalString = '';
let dic = {};
for (let char of str) {
if ('aeiou'.includes(char)) {
//check if dictionary has no certain vowel
//it happens when we first time meet a vowel
if (!(char in dic)) {
dic[char] = 0;
}
//increment our counter
dic[char]+=1;
}
}
//by the end of the loop
//we have object with { e: 1, o: 2 }
//now we need to gather values into string
//loop through the object
for ([char, count] of Object.entries(dic)) {
finalString += char + count;
}
return finalString;
};
console.log(vowelFrequency(text));
Shorter version of the same solution would be:
function vowelFrequency(str) {
const vowels = 'aeiou';
let dic = [...str].reduce((dic, char) => {
if (vowels.includes(char))
dic[char] = dic[char] + 1 || 1;
return dic;
}, {});
return Object.entries(dic)
.map(([char, count]) => `${char}${count}`)
.join('');
};
One concise approach would be to transform the string via String.prototype.replaceAll (evaluating every character in the string). The following code searches the original string (which you may wish to normalize beforehand with .toLowerCase() for better results) for any character.
"hello world".replaceAll(/./g, ( char, index, str ) =>
!'aeiou'.includes( char ) || str.lastIndexOf( char ) > index
? "" : char + [ ...str ].filter( o => o == char ).length
);
Each character is checked against a list of vowels. We also check to see if the character index is the last index of this character (does it appear multiple times) in the original string. If either of these conditions fail, an empty string is returned in the character's place.
If our character is in our vowel list, and is the last instance of itself, then we split the original string, filter-out non-matching characters, and return the final count of character instances.
The above approach is somewhat of a gimmick. It's concise, but probably not very self-explanatory or maintainable. Realistically, you'd want to take a slightly more verbose approach (see below).
Note that Map is preferred over a standard object to ensure that key-insertion order is preserved.
function charInstanceString ( input, chars = "aeiou" ) {
/**
* Cycle over each character in our string, checking
* if it appears in our `chars` string. If the character
* appears in our `chars` string, we'll update our map
* to reflect the number of instances for the character.
*/
const charMap = new Map();
for ( const char of input ) {
if ( !chars.includes( char ) ) continue;
charMap.set( char, charMap.get( char ) + 1 || 1 );
}
/**
* Cycle over our map, adding each character (and its
* corresponding count) to an output string.
*/
let output = "";
for ( const [ char, count ] of charMap ) {
output += `${ char }${ count }`;
}
return output;
}
Given a string and an integer k, you need to reverse the first k characters for every segment of length 2k characters counting from the start of the string. If there are less than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and left the other as original.
Example
Input: s = "abcdefg", k = 2
Output: "bacdfeg"
In the above example, the first chunk of two "ab" was reversed to "ba" and the third chunk of two "ef" was reversed to "fe".
This is my approach:
var reverseStr = function(s, k) {
for (let i = 0; i < k; i++) {
let temp = s[i];
s[i] = s[k - i - 1];
s[k - i - 1] = temp
}
return s
};
console.log(reverseStr("abcdefg", 2))
How do I produce the desired output?
One option is to use a regular expression to match up to k characters, followed by up to 2k characters - then use a replacer function to reverse only the initial k characters:
var reverseStr = function(s, k) {
const pattern = new RegExp(`(.{1,${k}})(.{0,${k}})`, 'g');
return s.replace(pattern, (_, g1, g2) => [...g1].reverse().join('') + g2);
};
console.log(reverseStr("abcdefg", 2))
Strings in JavaScript are immutable, you can't assign to their indexes to modify the string in place. You need to build a new string and return it.
You need a loop for each 2k group. Extract that substring, reverse the first k characters, then concatenate them to the result.
function reverseStr(s, k) {
let result = "";
for (let i = 0; i < s.length; i += 2*k) {
let chunk1 = s.substr(i, k);
// reverse first half
chunk1 = chunk1.split("").reverse().join("");
let chunk2 = s.substr(i+k, k);
result += chunk1 + chunk2;
}
return result;
}
console.log(reverseStr("12345678", 2));
You can realize it in three steps. First separate the string into k group. Then reverse the string of each group at an even index. Last join all the group together.
function reverseStr(s, k) {
return s
.replace(new RegExp('\\w{' + k + '}', 'g'), $2 => $2 + '|')
.split('|')
.map((item, i) =>
i % 2 !== 0
? item
: item
.split('')
.reverse()
.join('')
)
.join('');
}
console.log(reverseStr('abcdefg', 2));
Reverse string in chunks of size k
//make a variable string with some letters to test.
let str = "abcdefg";
//k represents is the number of letters to reverse starting at 0 to position k
let k = 4;
//select the first k letters and split on blankstring, converting to a list
//then reversing that list, collapsing it to a string with join, then
//re-append the original string at position k to the end.
result = (str.substring(0,k).split('')).reverse().join('') + str.substring(k);
console.log({result});
Prints:
{ result: 'dcbaefg' }
I am trying to create a function that takes in a string and changes each letters value to a "(" if the character is not duplicated in the string, and a ")" if the character does have a duplicate present in the string. I have decided to go an unconventional route to solve this problem but I am running in to an issue with a double for loop. From what I understand, the inner for loop in javascript does not have access to the variables outside of the loop. I want to loop through every item in an array twice but I'm not sure what to set the inner loops length as.
Here is my code:
function sortAndChange(word) {
const splitter = word.toLowerCase().split("");
//let jSplitter = word.toLowerCase().split("").length;
let endResult = "";
let truthArray = [];
for(i = 0; i < splitter.length; i++){
for(j = 0; j < splitter.length; j++){
console.log(j);
if(splitter[i] == splitter[j]){
truthArray.push(true);
} else {
truthArray.push(false);
}
}
console.log(truthArray);
truthArray.every(item => item === false) ? endResult += "(" : endResult += ")";
truthArray = [];
}
console.log(endResult);
}
Expected Result:
sortAndChange("Success") //expected output: ")())())"
sortAndChange("easy") //expected output: "(((("
You can do that in following steps:
Convert string to array using split and use map() on it.
Compare the indexOf() and lastIndexOf() to check if its duplicate or not.
Return the ) or ( based on ur condition. And then at last join the array
function sortAndChange(str){
let arr = str.toLowerCase().split('')
return arr.map(x => {
//if its not duplicated
if(arr.indexOf(x) === arr.lastIndexOf(x)){
return '('
}
//If its duplicated
else{
return ')'
}
}).join('');
}
console.log(sortAndChange("Success")) //expected output: ")())())"
console.log(sortAndChange("easy")) //expected output: "(((("
You could take a object and keep a boolean value for later mapping the values.
This approach has two loops with O(2n)
function sortAndChange(word) {
word = word.toLowerCase();
var map = [...word].reduce((m, c) => (m[c] = c in m, m), {});
return Array
.from(word, c => '()'[+map[c]])
.join('');
}
console.log(sortAndChange("Success")); // )())())
console.log(sortAndChange("easy")); // ((((
This can easily be achieved using a combination of regex and the map construct in javascript:
const input = "this is a test";
const characters = input.toLowerCase().split('');
const transformed = characters.map(currentCharacter => {
const regexpression = new RegExp(currentCharacter, "g");
if (input.toLowerCase().match(regexpression || []).length > 1) return ')'
return '(';
}).join("");
console.log(transformed);
Look at the following snippet and comments
function sortAndChange(str) {
// we create an array containing the characters on the string
// so we can use Array.reduce
return str.split('').reduce((tmp, x, xi) => {
// we look if the character is duplicate in the string
// by looking for instance of the character
if (str.slice(xi + 1).includes(x.toLowerCase())) {
// Duplicate - we replace every occurence of the character
tmp = tmp.replace(new RegExp(x, 'gi'), ')');
} else {
// Not duplicate
tmp = tmp.replace(new RegExp(x, 'gi'), '(');
}
return tmp;
}, str);
}
console.log(sortAndChange('Success')); //expected output: ")())())"
console.log(sortAndChange('Easy')); //expected output: "(((("
1) use Array.from to convert to array of chars
2) use reduce to build object with key-value pairs as char in string and ( or ) as value based on repetition .
3) Now convert original string to result string using the chars from above object.
function sortAndChange(str) {
const str_arr = Array.from(str.toLowerCase());
const obj = str_arr.reduce(
(acc, char) => ((acc[char] = char in acc ? ")" : "("), acc),
{}
);
return str_arr.reduce((acc, char) => `${acc}${obj[char]}`, "");
}
console.log(sortAndChange("Success")); // ")())())"
console.log(sortAndChange("easy")); // ((((
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));
I have this code :
//make first letter of each word capital
function titleCase(str) {
/*
* 1. change all letters to lower case
* 2. split words
* 3. set each 1st letter to Capital
* 4. combine array back into string
*/
arr = [];
str.toLowerCase();
arr = str.split(" ");
for (var index = 0; index < arr.length; index++) {
arr[index].charAt(0).toUpperCase();
}
str= arr.join(" ");
return str;
}
console.log(titleCase("Potato potato potato"));
And I don't understand why toLowerCase() and toUpperCase() are not working. What am I doing wrong ?
There are 2 updates required
Reassign str.toLowerCase() to str
Reassign updated array value back in array.
Please note, until and unless you reassign the values, the original value does not change. Hence, the result remained unaffected.
//make first letter of each word capital
function titleCase(str) {
/*
1. change all letters to lower case
2. split words
3. set each 1st letter to Capital
4. combine array back into string
*/
arr = [];
str = str.toLowerCase(); // **** Problem 1 - Reassigning
arr = str.split(" ");
for (var index = 0; index < arr.length; index++) {
// **** Problem 2 - Reassigning
arr[index] = arr[index].charAt(0).toUpperCase() + arr[index].slice(1);
}
str= arr.join(" ");
return str;
}
console.log(titleCase("Potato potato potato"));
You need to reassign (overwrite) the value inside the array after you change it. Otherwise, the array remains unchanged. Besides, you forgot to add the rest of the string (arr[index].slice(1)) to the uppercased letter.
function titleCase(str) {
let arr = [];
str.toLowerCase();
arr = str.split(" ");
for (var index = 0; index < arr.length; index++) {
arr[index] = arr[index].charAt(0).toUpperCase() + arr[index].slice(1); // <-- Changes
}
str= arr.join(" ");
return str;
}
console.log(titleCase("Potato potato potato"));
EDIT
Here is my own ES6 one-liner version :
titleCase = str => str.trim().split(" ").map( word => word.charAt(0).toUpperCase() + word.slice(1) ).join(" ")
console.log(titleCase("Potato potato potato"));
Explanation :
titleCase = str => str
.trim() // Removes extra spaces
.split(" ")
.map( word =>
word.charAt(0).toUpperCase() + word.slice(1) // Uppercases 1st letter, adds the rest of the word, returns the whole
)
.join(" ") // Reforms a string
you can simply do
function convert(str){
return str.split(' ').map(e => e.replace(/([A-Za-z])(\w+)/, (x, y, z) => y.toUpperCase()+z.toLowerCase())).join(' ');
}
console.log(convert('Potato potato pOtato'))
short solution:
var titleCase = (str)=>str.toLowerCase().split(' ').map(word=>word.charAt(0).toUpperCase()+word.slice(1)).join(' ');
calling:
titleCase('Potato potato potato');
Splitting the string by space and map a lambda to the resulting array. The lambda turns up the first letter and append the rest of the word.
as pointed out in the comments:
var titleCase = (str)=>str.toLowerCase().split(' ').reduce((currentString, word)=>currentString+(currentString ? ' ':'')+word.charAt(0).toUpperCase()+word.slice(1));
this works also and loops only one time.