Array manipulation error with regex - Kata 6 Codewar - javascript

In theory it should transform a given array to camel case. I don't understand what is wrong
function toCamelCase(str){
if(str.length === 0) return ""
let array = str.split(/([_-])/);
array.forEach(word =>{
word == "-" ? word.replace("") : word.charAt(0).toUpperCase()
})
return array
}

The .replace() method doesn't modify the word variable, it instead returns a new modified string. So your code is producing new values within the loop but doesn't do anything with those values. Moreover, word here is a value and not a reference to your array values, so you can't modify them directly from within your forEach() loop and expect it to modify the string values from your array. You instead need to create a new array, with each element transformed, which can be done by using .map() and returning the new value:
function toCamelCase(str) {
const array = str.split(/[_-]/);
return array.map((word, i) => {
return i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)
}).join("");
}
console.log(toCamelCase("this-is-some-text"));
Note that you can remove the capturing group from your .split() to remove the _ and - chars from your array so that you don't need to remove them when you map.
Note that for something like this, if you're already using regular expressions in your .split(), you might consider using .replace() with a replacement function, for example, something like:
function toCamelCase(str) {
return str.replace(/-\w/g, ([,m]) => m.toUpperCase());
}
console.log(toCamelCase("this-is-some-text"));

word == "-" ? word.replace("") : word.charAt(0).toUpperCase() is just a ternary statement floating in space. This code isn't altering any variables. It's equivalent to:
if(word == "-"){
word.replace("")
}
else{
word.charAt(0).toUpperCase()
}
Really, you don't need to mess with arrays if you make use of .replace()'s callback function.
function toCamelCase(str) {
// Match underscore or dash followed by a letter, case-insensitively
// Store the letter in a capture group; $1 in this case
return str.replace( /[_-]([a-z])/gi, function( matches ) {
// Uppercase the letter
return matches[ 1 ].toUpperCase()
} );
}
console.log( toCamelCase( 'to-be_Camel_cased' ) );

Related

a bit clarafication on using spread and string manipulation, foreach loop

so I've wrote this function, i want to uppercase the vowels and lowercase every other letter,
problem the end result ends with the same string, I'm new to spread and for-each,
after i spread a string does it become an array?
when i manipulate letters does it suppose to become a string again with the manipulations or do i need to join it? why aren't the upper and lowercase functions don't work?
the function:
function upperCase(str) {
var vowels = "aeiou";
[...str].forEach(letter => {
if (vowels.includes(letter)) letter.toUpperCase();
letter.toLowerCase();
});
console.log(str);
}
You have several problems:
.toUpperCase and toLowerCase return the new value, they don't mutate the existing value (and strings are immutable anyway)
Even if they did mutate the existing value, they'd change the letter string in the array and not the original string
You didn't use else to toLowerCase always runs
You need to:
return a value
Use map to collect the values
Use join() to turn the array back into a string
Such:
function upperCase(str) {
const vowels = "aeiou";
const result =
[...str]
.map(
letter =>
(vowels.includes(letter))
? letter.toUpperCase()
: letter.toLowerCase()
).join("");
console.log(result);
}
upperCase("The quick brown fox jumps over the lazy dog");
You need to assign the result of your foreach to something.
function upperCase(str) {
var vowels = "aeiou";
[...str].forEach(letter => {
if (vowels.includes(letter)) letter.toUpperCase();
letter.toLowerCase();
});
console.log(str);
}
[...str] is creating an array, looping over it, preforming an action, but then not saving the resulting array to any variable at the end. You're also missing an else and/ or a return. I think a map also makes more sense in this case.
function upperCase(str) {
var vowels = "aeiou";
const result = [...str].map(letter => {
if (vowels.includes(letter)) return letter.toUpperCase();
return letter.toLowerCase();
});
console.log(result);
}
If you just want to manipulate a string you might want to use the replace function
const newString = str.toLowerCase().replace(/[a,e,i,o,u]/g, letter => letter.toUpperCase())
This first puts everything to lower case, and afterwards replaces all vowels (matching the regular expression) by their upper case versions.

Javascript: Map function weird behaviour

I am using the below code to identify if a character is duplicated, if it is then I replace with a specific char, else another char.
This code works
function dup(str) {
return str
.toLowerCase()
.split("")
.map((index, nonsense, s) => {
console.log(s);
return s.indexOf(index) == s.lastIndexOf(index) ? "(" : ")";
})
.join("");
}
But I do not understand why, the variable 'nonsense' makes it work. If you remove that unused var I get errors.
How can an unused var affect how map works?
The problem is with the order of the arguments and the value at specific locations
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, index, self) => { // the order is char, index and the current array in the 3rd argument
console.log(self);
return self.indexOf(char) == s.lastIndexOf(char) ? "(" : ")";
})
.join("");
}
If you wish to remove the issue with unused variable (for example, say with eslint) use the _ or prefix with _
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, _, self) => { // the order is char, index and the current array in the 3rd argument
console.log(self);
return self.indexOf(char) == self.lastIndexOf(char) ? "(" : ")";
})
.join("");
}
EDIT
Though unrelated,a more optimal way to do it would be to use Set to deduplicate.
function dup(str) {
return Array.from(new Set(str
.toLowerCase()
.split(""))
.join("");
}
This has less to do with .map() and more to do with receiving arguments.
The function passed to .map is automatically passed 3 arguments (which you are calling index, nonsense, and s here). In JavaScript, you are not required to specifically capture any of them with argument names, but if you want to use the second or third one, you will need to provide some argument name(s) for the ones you are going to skip over to indicate that you are interested in the third argument.
Having said that, the arguments passed to .map() (in order) are: element, index, array and your names suggest that you believe it's: index, element, array. So a better naming convention would be as shown below:
function dup(str) {
return str
.toLowerCase()
.split("")
.map((char, index, ary) => {
console.log(ary);
return ary.indexOf(index) == ary.lastIndexOf(index) ? "(" : ")";
})
.join("");
}
dup("The quick quick brown fox.");
in your code index represent the current char, nonsense represents the indexOf that char at s array. if you remove nonsense index will be represent the char and s will be represent the indexOf that char. that cause the error because you are trying to do s.indexOf(index) while s is of type number
try to do it like this
function dup(str) {
return str
.toLowerCase()
.split('')
.map(s => {
console.log(s)
return str.indexOf(s) == str.lastIndexOf(s) ? '(' : ')'
})
.join('')
}
From the docs:
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
As you can see, the map function takes up to three params, where the second and third are optional. Your variables index, nonsense and s are filling in for the current value, the actual index and the array. Remove nonsense and your s variable becomes the current index of map, instead of your array.
That is the expected behavior.

Don't understand why replace() method is not working as expected

Working on the following problem:
Create a function called alienLanguage where the input will be a str and the output should capitalize all letters except for the last letter of each word
alienLanguage("My name is John") should return "My NAMe Is JOHn"
This is what I have coded:
function alienLanguage(str){
var words = str.toUpperCase().split(' ').map(function (a) {
return a.replace(a[a.length - 1], a[a.length - 1].toLowerCase())
});
return words.join(' ');
}
All of the example test cases work except for the following:
Expected: '\'THIs Is An EXAMPLe\'', instead got: '\'THIs Is An eXAMPLE\''
Why is the e in eXAMPLE turning lowercase? Shouldn't everything automatically turn upperCase ?
edit: I just realized that there are 2 e's and the first one is being replaced. How can I replace the last e? and isn't the last character specified already?
isn't the last character specified already?
No, you've only specified what character to replace, not where. replace searches the string for the expression.
How can I replace the last e?
Don't use replace at all. It does construct a new string anyway, and you can do that much easier:
function alienLanguage(str) {
return str.split(' ').map(function(a) {
return a.slice(0, -1).toUpperCase() + a.slice(-1).toLowerCase();
}).join(' ');
}
You also could use replace, but would use it with regular expression:
function alienLanguage(str) {
return str.toUpperCase().replace(/.\b/g, function(last) {
// ^^^^^^ matches all characters before a word boundary
return last.toLowerCase();
});
}
You don't need to split the string into words. Just use a positive lookahead assertion in your regex to upper-case all letters that are immediately followed by another letter, like this:
function alienLanguage(str){
return str.replace(/(\w)(?=\w)/g, l => l.toUpperCase())
}
You can use substring. Your replace replaces and replaces the first occurrence of the last character in the words -- definitely what you're after:
function alienLanguage(str){
var words = str.toUpperCase().split(' ').map(function (a) {
return a.length ? a.substring(0, a.length - 2) + a[a.length - 1].toLowerCase() : a;
});
return words.join(' ');
}
As #TatsuyukiIshi said, you need to change the use of replace() for regular assignment. For extra help, here is an updated version of your alienLanguage function:
function alienLanguage(str){
return str.toUpperCase().split(' ').map(function(word) {
return word.slice(0, -1) + word.substr(-1).toLowerCase()
}).join(' ')
}
Fiddle here
Extra notes:
word.slice(0,-1) returns the word minus the last character.
word.substr(-1) returns the last character as a string.
.map() returns an array, so you can call join() directly on the result.
replace() is searching using a pattern (or regular expression) and it replaces all occurrences.
Instead, you want to truncate and create a new string, which is known as assignment in other languages (but strings in JS are immutable). See here for more details.
Try it.
function alienLanguage(str){
var words = str.toUpperCase().split(' ').map(function (a) {
return a.substr(0, a.length - 1) +a[a.length - 1].toLowerCase() + a.substr(a.length - 1 + 1);
});
return words.join(' ');
}
Instead of searching for the letter, just concatenate the uppercase part of the string up to the last letter with the lowercased last letter.
This should do the trick:
function alienLanguage(str) {
return str.split(' ').map(function(a) {
return a.slice(0, -1).toUpperCase() + a.slice(-1).toLowerCase();
}).join(' ');
}

Javascript wrong outcome when crafting a custom regex camelCase solution for strings

I'm attempting a Javascript challenge who's instructions are:
Complete the method/function so that it converts dash/underscore delimited
words into camel casing. The first word within the output should be
capitalized only if the original word was capitalized.
Examples:
toCamelCase("the-stealth-warrior")
// returns "theStealthWarrior"
toCamelCase("The_Stealth_Warrior")
// returns "TheStealthWarrior"
My solution is:
function toCamelCase(str) {
console.log(str);
var camel = str.replace(/(?:^\w|[A-Z]|-\w|_\w)/g, function(letter, index) {
return index === 0 && letter === letter.toLowercase ?
letter.toLowercase : letter.toUpperCase();
}).replace(/(-|_)/g, "");
console.log(camel);
return camel;
}
and the output when using my code with the test cases is:
toCamelCase('the_stealth_warrior') did not return correct value -
Expected: theStealthWarrior, instead got: TheStealthWarrior
any ideas where this is going wrong? I feel my conditions in the ternary operator should be returning a lowercase t.
This bit of code here is causing your problem:
function(letter, index) {
return index === 0 && letter === letter.toLowercase ?
letter.toLowercase : letter.toUpperCase();
}
You probably meant to use toLowerCase(), but instead you've provided a reference to a non-existent property of letter. Since toLowercase doesn't exist, it will return undefined which will cause your conditional to always return false.
Change the line to:
function(letter, index) {
return index === 0 && letter === letter.toLowerCase() ?
letter.toLowerCase() : letter.toUpperCase();
}
How about simplifying it a bit to this:
function toCamelCase(str) {
return str.replace(/[-_](.?)/g, function(match, p1) {
return p1.toUpperCase();
})
}
document.write(toCamelCase("the-stealth-warrior") + "<br>");
document.write(toCamelCase("The_Stealth_Warrior") + "<br>");
Explanation:
[-_] Find either a - or _
(.?) Followed by any other character and put this other character in a group.
Then call .replace() on that with a custom callback using the g flag to do all matches.
The custom callback will be passed the full match as the first argument and any groups in the match as the subsequent arguments. Since what we want to convert this to is just the uppercase version of the first group, we just uppercase the second argument with return p1.toUpperCase() and then the whole match is replaced by an upper case version of the first matched group. This then converts _x to X.
This skips the leading character because there's no - or _ before it.
This skips any trailing - or _ because there's no character after it.

Regex for alternating indexes from a word boundary

I've just finished this programming challenge on CodeWars:
Write a function toWeirdCase that accepts a string, and returns the same string with all even indexed characters in each word upper cased, and all odd indexed characters in each word lower cased. The indexing just explained is zero based, so the zero-ith index is even, therefore that character should be upper cased.
I tried to figure this out with regex before giving up and simply using a for loop with indexes. Just to confirm, the index of capitalising letters resets to 0 whenever there is a space. So ThIs Is A CoRrEcT AnSwEr but ThIs iS nOt, because every first letter of each word must be capitalised.
With that in mind, is there an approach to look for alternate (odd or even) indexes using regex? In this case, find a word boundary using \b, and then every even index from there, until the end of the word?
You can borrow map to convert apply a function to each character, i%2 to detect if i is odd, and toLowerCase or toUpperCase to change the case:
function toWeirdCase(str) {
return [].map.call(str, function(char, i) {
return char[i%2 ? 'toLowerCase' : 'toUpperCase']();
}).join('');
}
There are multiple ways to reset the index at each space. For example,
function toWeirdCase(str) {
var i = 0;
return [].map.call(str, function(char) {
if(char === ' ') i = -1;
return char[i++ % 2 ? 'toLowerCase' : 'toUpperCase']();
}).join('');
}
The function parameter to replace receives the match offset after the matched string and matched groups.
function toWeirdCase(s) {
return s.replace(/[a-zA-Z]/g, function (ltr, offset) {
return offset & 1 ? ltr.toLowerCase() : ltr.toUpperCase();
});
}
You need to split the input into words, then weird case each word:
function toWeirdCase(str) {
return str.split(' ').map(weirdCaseWord).join(' ');
}
weirdCaseWord can be written as in the other answer:
function weirdCaseWord(str) {
return str.split('').map(function(char, i) {
return char[i%2 ? 'toLowerCase' : 'toUpperCase']();
}).join('');
}
If you prefer, you could use a single map with a flag which is toggled on each character, and reset on a space:
function toWeirdCase(str) {
var even = false;
return str.split('').map(function(char) {
even = char === ' ' ? false : !even;
return char[even ? 'toUpperCase' : 'toLowerCase']();
}).join('');
}

Categories

Resources