Say I have a function that transforms kebab-case to camelCase:
camelize("my-kebab-string") === 'myKebabString';
I'm almost there, but my code outputs the first letter with uppercase too:
function camelize(str){
let arr = str.split('-');
let capital = arr.map(item=> item.charAt(0).toUpperCase() + item.slice(1).toLowerCase());
let capitalString = capital.join("");
console.log(capitalString);
}
camelize("my-kebab-string");
You can also try regex.
camelize = s => s.replace(/-./g, x=>x[1].toUpperCase())
Looks only for hyphen followed by any character, and capitalises it and replaces the hyphen+character with the capitalised character.
To keep your existing code, I've just added a check on the index that will return item instead of the transformed item if item is 0 (falsy), since the problem is just that you are upper-casing the first item as well, while you shouldn't.
In a nutshell, the inline expression becomes: (item, index) => index ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() : item, because:
If index is not falsy (so, if index is > 0 in your context), the capitalized string is returned.
Otherwise, the current item is returned.
Of course, this could be cleaner and likely single line, but I wanted to stay as close as possible to your code so that you could understand what was wrong:
function camelize(str){
let arr = str.split('-');
let capital = arr.map((item, index) => index ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() : item.toLowerCase());
// ^-- change here.
let capitalString = capital.join("");
console.log(capitalString);
}
camelize("my-kebab-string");
As a side note, you could've found a potential cleaner answer here: Converting any string into camel case
For lodash users:
_.camelCase('my-kebab-string') => 'myKebabString'
The first method is to just transform to lower case the first entry of your capital array, like this:
capital[0] = capital[0].toLowerCase();
Another method, which I think to be more efficient, is to pass another parameter to the map callback, which is the index. Take a look at this for further reading:
https://www.w3schools.com/jsref/jsref_map.asp
So you transform to upper case only if (index > 0).
Like this:
let capital = arr.map((item, index) => index ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() : item);
so I tried both array-string and regex but regex is slower !
const string = " background-color: red; \n color: red;\n z-index: 10"
// regex
console.time("regex")
let property = string
const camelProp = property.replace(/(-[a-z])/, (g) => {
return g.replace("-", "").toUpperCase()
})
console.timeEnd("regex")
// custom
console.time("custom")
const str = string
let strNew = str
.split("-")
.map((e) => {
return e[0].toUpperCase() + e.slice(1)
})
.join("")
console.timeEnd("custom")
console.log(camelProp)
console.log(strNew)
Related
I was taking on a JS challenge to take a first/last name string input and do the following:
swap the first letter of first/last name
convert all characters to lowercase, except for the first characters, which need to be uppercase
Example:
input: DonAlD tRuMp
output: Tonald Drump
The following is the code I came up with:
const input = prompt("Enter a name:")
function switchFirstLetters(input) {
let stringArray = input.split('');
for(let i=0; i < stringArray.length; i++) {
if(stringArray[i - 1] === ' ') {
[stringArray[0], stringArray[i]] = [stringArray[i], stringArray[0]]; // destructuring
}
}
return result = stringArray.join('');
}
let swappedString = switchFirstLetters(input);
function capFirstLetters(swappedString) {
let stringArray = swappedString.toLowerCase();
stringArray = stringArray.split('');
stringArray[0] = stringArray[0].toUpperCase();
for(let i=0; i < stringArray.length; i++) {
if(stringArray[i - 1] === ' ') {
stringArray[i] = stringArray[i].toUpperCase();
}
}
return result = stringArray.join('');
}
let finalString = capFirstLetters(swappedString);
console.log(finalString);
My thought process for the switchFirstLetters function was:
Create an array from the string parameter
Run through the array length. If the value of the element prior the current element is equal to ' ', use destructuring to swap the current element with the element at index 0
Concatenate elements into a new string and return that value
My thought process for the capFirstLetters function:
Convert all characters in the string to lowercase (this could be handled outside of the function as well)
Create an array from the new, lowercase string
Make character at index 0 be uppercase (this could also be integrated into the for loop)
Run through the array length. If the value of the element prior to the current element is equal to ' ', convert that element to uppercase.
Concatenate array elements into a new string
The code works, but I'm still early in my coding journey and realize it's likely not an ideal solution, so I was wondering if anyone here could help me optimize this further to help me learn. Thanks!
You could also use a regular expression to replace the first letters:
let name = "DonAlD tRuMp";
let result = name.toLowerCase().replace(/(\S)(\S*\s+)(\S)/g, (_, a, b, c) =>
c.toUpperCase() + b + a.toUpperCase()
);
console.log(result);
The regular expression uses \S (a non-white-space character), \S* (zero or more of those), \s+ (one or more white-space characters) and parentheses to create capture groups. These three groups map to a,b,c parameters in the callback function that is passed to replace as second argument. With these parts the replacement string can be constructed. Both the capitalisation and the switch happen in the construction.
If the replace function is a little overwhelming, my attempt introduces the for-of loop, the substring string method, array slice as well as the && short circuit evaluation. You should also be aware you can access a given character of a string using the square bracket syntax, just like array, but string has it's own set of methods which tend to have different names.
Definitely take a look at the replace function, to make your v2.
const rawNameInput = "DonAlD jUnior tRuMp"
const nameInput = rawNameInput.trim()
const rawNameWords = nameInput.split(" ")
const nameWords = []
for (const word of rawNameWords) {
const first = word[0].toUpperCase()
const rest = word.substring(1).toLowerCase()
nameWords.push(first + rest)
}
const middleNames = nameWords.slice(1, -1).join(" ")
const lastIdx = nameWords.length - 1
const newFirstName = nameWords[lastIdx][0] + nameWords[0].substring(1)
const newLastName = nameWords[0][0] + nameWords[lastIdx].substring(1)
console.log(`${newFirstName} ${middleNames && middleNames + " "}${newLastName}`)
This question already has answers here:
Counting the vowels in a string using Regular Expression
(2 answers)
Closed 1 year ago.
I tried to write a function which checks if a given string contains vowels and I cannot see why it works for some words 'cat' and 'why' but not 'DOG', i believe that i have accounted for uppercase.
const containsVowels = string => {
var lowerCase = string.toLowerCase();
var word = lowerCase.split("");
var vowelsArray = ["a","o","i","u","y"];
const result = word.filter(letter => vowelsArray.includes(letter));
return result.includes("a","o","i","u","y");
};
includes takes only 2 parameters, the first one being searchElement and second parameter being fromIndex.
Reference : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#parameters
You wouldn't want to do the last check if the result array contains vowels or not, because in the previous step itself you are filtering out the word to get array that contains only vowels. So just check if the array is empty or it contains any elements inside it.
const containsVowels = str => {
let lowerCase = str.toLowerCase();
let word = lowerCase.split("");
let vowelsArray = ["a","o","i","u","y"];
const result = word.filter(letter => vowelsArray.includes(letter));
return result.length > 0;
};
console.log(containsVowels("cat"));
console.log(containsVowels("DOG"));
console.log(containsVowels("BCDF"));
Suggestion: Don't use built in keywords as variables.
As pointed out by Muhammad, we can regex to find if the string contains vowels
const containsVowel = str => {
const vowelRegex = /[aeiou]/i;
return vowelRegex.test(str);
};
2 Problems,
Why would you use includes twice ?
&
You cannot use includes like
result.includes("a","o","i","u","y");
includes only accepts 2 param:
includes(searchElement, fromIndex)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
By filtering, you already know the result.
What you should do is, compare the length of the result:
const containsVowels = string => {
let lowerCase = string.toLowerCase();
let word = lowerCase.split("");
let vowelsArray = ["a","o","i","u","y"];
const result = word.filter(letter => vowelsArray.includes(letter));
return result.length > 0 ? true : false
};
use regex to get the result.
var regEx = /[aeiou]/gi
var test_string = "Cat";
var match = test_string.match(regEx)
if(match)
console.log("Match found", match)
when you write something like this
result.includes("a","o","i","u","y")
this compare with only it's first element which is "a" and one thing you don't need to write the above mentioned code further.
After filtering just replace the above code with
return result.length > 0 ? true : false
I am trying to make the first letter of each word capitalized via toUpperCase method and the rest of the word is in the lower case via the toLowerCase method. But I am missing something... Why temp value is not matching with result[1][0] even if I am using that method for both?
Note: I know about other ways (map, replace, etc) for my solution, but I want to just use a for-loop with toUpperCase and toLowerCase methods.
function titleCase(str) {
let regex = /[^0-9\s]+/g;
var result = str.match(regex);
let temp = "";
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result[i].length; j++) {
result[1][0] = result[1][0].toUpperCase();
temp = result[1][0].toUpperCase();
}
}
console.log(temp); // Output is 'A'
console.log(result[1][0]); //Output is 'a'
// Normally 'temp' and 'result[1][0]' should be equal, but one returns a lowercase character and the other an uppercase character.
return str;
}
titleCase("I'm a little tea pot");
Your problem is not with the toUppercase(), it is with the reference.
When referencing result[1][0], why are you including the 0? You already have the second character with result[1]
result[1] === 'a'. No need to include the [0] as well.
Change your code so it looks like this:
function titleCase(str) {
let regex = /[^0-9\s]+/g;
var result = str.match(regex);
let temp = "";
result[1] = result[1].toUpperCase();
temp = result[1].toUpperCase();
console.log(temp); // Output is 'A'
console.log(result[1]); //Output is also 'A'
// both now equals capital A
return str;
}
titleCase("I'm a little tea pot");
EDIT:
Updating the function to uppercase the first letter of the word.
We can use ES6, which would make this really simple:
const capitalize = (string = '') => [...string].map((char, index) => index ? char : char.toUpperCase()).join('')
Use it: capitalize("hello") returns 'Hello'.
First we convert the string to an array, using the spread operator, to get each char individually as a string. Then we map each character to get the index to apply the uppercase to it. Index true means not equal 0, so (!index) is the first character. We then apply the uppercase function to it and then return the string.
If you want a more object oriented approach, we can do something like this:
String.prototype.capitalize = function(allWords) {
return (allWords) ?
this.split(' ').map(word => word.capitalize()).join(' ') :
return this.charAt(0).toUpperCase() + this.slice(1);
}
Use it: "hello, world!".capitalize(); returns "Hello, World"
We break down the phrase to words and then recursive calls until capitalising all words. If allWords is undefined, capitalise only the first word meaning the first character of the whole string.
I was tried to change a specific character in the string but strings are immutable in JS so this does not make sense.
I'd like to transform a string like:
hello!world.what?up into ["hello!", "world.", "what?", "up"]
.split(/[?=<\.\?\!>]+/) is close to what I'm after, which returns:
["hello", "world", "what", "up"]
.split(/(?=[\?\!\.])/) is a bit closer yet, which returns:
["hello", "!world", ".what", "?up"]
This does the trick, but it's not pretty:
.split(/(?=[\?\!\.])/).map((s, idx, arr) => { (idx > 0) s = s.slice(1); return idx < arr.length - 1 ? s + arr[idx+1][0] : s }).filter(s => s)
How would I rephrase this to achieve the desired output?
Edit: Updated question.
Not sure of the real requirement but to accomplish what you want you could use .match instead of .split.
const items =
'hello!world.what?'.match(/\w+\W/g);
console.log(items);
update after comment
You could add a group for any character you want to use as the terminator for each part.
const items =
'hello!world.what?'.match(/\w+[!.?]/g);
console.log(items);
additional update
the previous solution would only select alphanumeric chars before the !.?
If you want to match any char except the delimiters then use
const items =
'hello!world.what?up'.match(/[^!.?]+([!.?]|$)/g);
console.log(items);
One solution could be first to use replace() for add a token after each searched character, then you can split by this token.
let input = "hello!world.what?";
const customSplit = (str) =>
{
let token = "#";
return str.replace(/[!.?]/g, (match) => match + "#")
.split(token)
.filter(Boolean);
}
console.log(customSplit(input));
I have a string that I need to split by a certain delimiter and convert into an array, but without removing the delimiter itself.
For example, consider the following code:
var str = "#mavic#phantom#spark";
str.split("#") //["", "mavic", "phantom", "spark"]
I need the output to be as follows:
["#mavic", "#phantom", "#spark"]
I read here but that does not answer my question.
You could split by positive lookahead of #.
var string = "#mavic#phantom#spark",
splitted = string.split(/(?=#)/);
console.log(splitted);
Split the string by # and use the reduce to return the modified string
var str = "#mavic#phantom#spark";
let x = str.split("#").reduce((acc, curr) => {
if (curr !== '') {
acc.push('#' + curr);
}
return acc;
}, [])
console.log(x)
Here is also some non-regexp methods of solving your task:
Solution 1 classical approach - iterate over the string and each time when we find indexOf our delimiter, we push to the result array the substring between current position and the next position. In the else block we have a case for the last substring - we simply add it to the result array and break the loop.
const delimiter = '#';
const result1 = [];
let i = 0;
while (i < str.length) {
const nextPosition = str.indexOf(delimiter, i+1);
if (nextPosition > 0) {
result1.push(str.substring(i, nextPosition));
i = nextPosition;
} else {
result1.push(str.substring(i));
break;
}
}
Solution 2 - split the initial string starting at index 1 (in order to not include empty string in the result array) and then just map the result array by concatenating the delimiter and current array item.
const result2 = str.substr(1).split(delimiter).map(s => delimiter + s);
another way:
filter empty elements after splitting, and map these elements to start with the character you splitted with.
string.split("#").filter((elem) => elem).map((elem) => "#" + elem);