https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-algorithm-scripting/title-case-a-sentence
Hi,
I'm practicing a basic algorithm from Freecodecamp.com.
The instruction is below.
Return the provided string with the first letter of each word
capitalized. Make sure the rest of the word is in lower case.
For the purpose of this exercise, you should also capitalize
connecting words like "the" and "of".
And this is my code.
const titleCase = str => {
let result = str.split(' ');
result = result.map(t => {
t = t.toLowerCase();
t[0] = t[0].toUpperCase();
return t;
});
return result.join(' ');
};
console.log(titleCase("I'm a little tea pot"));
Result
i'm a little tea pot
This is what I thought.
Extract a whole string into pieces by whitespace.
Make it lowercase.
Make only first letter uppercase.
return the result.
However, a problem is line:5 doesn't affect the result. The first letter doesn't change.
Am I missing something, right? It seems I misunderstand some concepts about map method.
Could you give some advice to fix that?
Thanks in advance.
The reason your code doesn't work as you would think is documented in the MDN String documentation.
When using bracket notation for character access, attempting to delete or assign a value to these properties will not succeed. The properties involved are neither writable nor configurable.
The alternative approach is to concatentate the first character after being converted to uppercase with the remaining string (excluding first character).
const titleCase = str => {
return str.toLowerCase()
.split(' ')
.map(w => `${w[0].toUpperCase()}${w.substring(1)}`)
.join(' ');
};
console.log(titleCase("I'm a little tea pot"));
Looks like the assignment on strings doesn't work like that in Javascript, too bad, it's intuitively correct.
So, use a string splice, which doesn't exist, so split the string into an array of characters with str.split(''), and use array splice.
The splice modifies the input array in place, so you don't need to re-assign it.
const str = "hello";
var rayy = str.split(''); // ['h', 'e', 'l', 'l', 'o']
rayy.splice(0, 1, rayy[0].toUpperCase()); // returns ['h], but rayy is now modified in place to ['H', 'e', 'l', 'l', 'o']
const str2 = rayy.join(''); // "Hello"
Your usage of map is adept and apt.
very elegantly looks with methods chaining - split+map+join
and patterns of string `${var1}${var2}` - in left back quote " ` "
var1 - item[0].toUpperCase() - first symbol toUpperCase
var2 - item.substring(1) - killing first symbol
.toLowerCase() - other symbols toLowerCase
const titleCase = (str) => str.split(' ')
.map(item => (`${item[0].toUpperCase()}${item.substring(1).toLowerCase()}`))
.join(' ')
console.log(titleCase("I'm a litTLE tea pot"));
Related
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.
I am having trouble with trying to find the last vowel in a string in JavaScript. I found out how to find the first vowel of a string and tried to modify that code but I'm stuck. I tried to edit the var vowels and I changed the 0 to -1 and vice versa but nothing worked.
Here's my code:
function endVowel(x){
var vowels = ("aeiouAEIOU");
return vowels.indexOf(x[-1]) !== 0;
}
What am I missing or doing wrong?
Use a regular expression to match a vowel, while using negative lookahead for other vowels:
function endVowel(x){
const match = x.match(/[aeiou](?!.*[aeiou])/i);
return match ? match[0] : 'No match';
}
console.log(endVowel('foobar'));
function findLastVowel(string) {
let pattern = /[aeiouAEIOU]/gim;
let result = [...string.match(pattern)]
return result[result.length - 1]
}
console.log(findLastVowel("your string here"))
vowels.indexOf(x[-1]) attempts to look for the last character in x (in fact, x[x.length-1] or x.slice(-1) is the correct syntax), but if this doesn't happen to be a vowel, it won't work. You'd need to iterate backwards from the end to test other characters in such a scenario.
To get the last vowel's index, you can strip non-vowels from the right using a regex and return the length - 1:
const lastVowel = s => s.replace(/[^aeiou]*$/i, "").length - 1;
[
"foobar",
"cdgh",
"abb",
"baabbba"
].forEach(e => console.log(`"${e}" => ${lastVowel(e)}`));
If you only want the last vowel (this is less useful than having the index, which essentially gives you both), pattern match on vowels and return the last element:
const lastVowel = s => (s.match(/[aeiou]/ig) || "").slice(-1);
[
"foobar",
"cdgh",
"abb",
"beobbba"
].forEach(e => console.log(`"${e}" => "${lastVowel(e)}"`));
One technique would be to build your endVowel atop your startVowel, using a string-reversing helper function. That's often a general principle when working with first and last values on something you can easily reverse.
Here's an example (and note that the endVowel does not in any way depend on the implementation of startVowel, only on its behavior):
const startVowel = (str, vowels = "aeiouAEIOU") =>
str .split('') .find (c => vowels .includes (c))
const reverseString = (str) => str .split('') .reverse () .join('')
const endVowel = (str) => startVowel (reverseString (str))
console .log (
startVowel ('The quick brown fox jumped over the lazy dog'), //=> 'e'
endVowel ('The quick brown fox jumped over the lazy dog'), //=> 'o'
)
Obligatory pun for English speakers: Q: What are you when you can't have a vowel movement? A: consonated.
This will give you the last vowel character in the string (keeping the original upper/lower case). If you're just looking for the index of the last vowel, that is indexOfLastVowel, of course!
function endVowel(x){
var y = x.toLowerCase();
var indexOfLastVowel = Math.max(y.lastIndexOf("a"), y.lastIndexOf("e"), y.lastIndexOf("i"), y.lastIndexOf("o"), y.lastIndexOf("u"));
return x.charAt(indexOfLastVowel);
}
console.log(endVowel("Find the last vowel in this string..."));
If no vowel is in the string, an empty string ("") is returned.
I know that we have a question similar to this but not quite the same.
I'm trying to make my function work which takes in a string as an argument and converts it to snake_case . It works most of the time with all the fancy !?<>= characters but there is one case that it can't convert and its camelCase .
It fails when I'm passing strings like snakeCase. It returns snakecase instead of snake_case.
I tried to implement it but I ended up just messing it up even more..
Can I have some help please?
my code:
const snakeCase = string => {
string = string.replace(/\W+/g, " ").toLowerCase().split(' ').join('_');
if (string.charAt(string.length - 1) === '_') {
return string.substring(0, string.length - 1);
}
return string;
}
You need to be able to detect the points at which an upper-case letter is in the string following another letter (that is, not following a space). You can do this with a regular expression, before you call toLowerCase on the input string:
\B(?=[A-Z])
In other words, a non-word boundary, followed by an upper case character. Split on either the above, or on a literal space, then .map the resulting array to lower case, and then you can join by underscores:
const snakeCase = string => {
return string.replace(/\W+/g, " ")
.split(/ |\B(?=[A-Z])/)
.map(word => word.toLowerCase())
.join('_');
};
console.log(snakeCase('snakeCase'));
Let's try that again Stan... this should do snake_case while realising that CamelCASECapitals = camel_case_capitals. It's basically the accepted answer with a pre-filter.
let splitCaps = string => string
.replace(/([a-z])([A-Z]+)/g, (m, s1, s2) => s1 + ' ' + s2)
.replace(/([A-Z])([A-Z]+)([^a-zA-Z0-9]*)$/, (m, s1, s2, s3) => s1 + s2.toLowerCase() + s3)
.replace(/([A-Z]+)([A-Z][a-z])/g,
(m, s1, s2) => s1.toLowerCase() + ' ' + s2);
let snakeCase = string =>
splitCaps(string)
.replace(/\W+/g, " ")
.split(/ |\B(?=[A-Z])/)
.map(word => word.toLowerCase())
.join('_');
> a = ['CamelCASERules', 'IndexID', 'CamelCASE', 'aID',
'theIDForUSGovAndDOD', 'TheID_', '_IDOne']
> _.map(a, snakeCase)
['camel_case_rules', 'index_id', 'camel_case', 'a_id', 'the_id_for_us_gov_and_dod',
'the_id_', '_id_one']
// And for the curious, here's the output from the pre-filter:
> _.map(a, splitCaps)
['Camel case Rules', 'Index Id', 'Camel Case', 'a Id', 'the id For us Gov And Dod',
'The Id_', '_id One']
Suppose the string is Hello World? and you want the returned value as hello_world? (with the character, then follow the below code)
const snakeCase = (string) => {
return string.replace(/\d+/g, ' ')
.split(/ |\B(?=[A-Z])/)
.map((word) => word.toLowerCase())
.join('_');
};
Example
snakeCase('Hello World?')
// "hello_world?"
snakeCase('Hello & World')
// "hello_&_world"
EDIT: It turns out this answer isn’t fool proof. The fool, being me ;-) Please check out a better one by Orwellophile here: https://stackoverflow.com/a/69878219/5377276
——
I think this one should cover all the bases 😄
It was inspired by #h0r53's answer to the accepted answer. However it evolved into a more complete function, as it will convert any string, camelCase, kebab-case or otherwise into snake_case the way you'd expect it, containing only a-z and 0-9 characters, usable for function and variable names:
convert_to_snake_case(string) {
return string.charAt(0).toLowerCase() + string.slice(1) // lowercase the first character
.replace(/\W+/g, " ") // Remove all excess white space and replace & , . etc.
.replace(/([a-z])([A-Z])([a-z])/g, "$1 $2$3") // Put a space at the position of a camelCase -> camel Case
.split(/\B(?=[A-Z]{2,})/) // Now split the multi-uppercases customerID -> customer,ID
.join(' ') // And join back with spaces.
.split(' ') // Split all the spaces again, this time we're fully converted
.join('_') // And finally snake_case things up
.toLowerCase() // With a nice lower case
}
Conversion examples:
'snakeCase' => 'snake_case'
'CustomerID' => 'customer_id'
'GPS' => 'gps'
'IP-address' => 'ip_address'
'Another & Another, one too' => 'another_another_one_too'
'random ----- Thing123' => 'random_thing123'
'kebab-case-example' => 'kebab_case_example'
There is method in lodash named snakeCase(). You can consider that as well.
https://lodash.com/docs/4.17.15#snakeCase
Orwellophile's answer does not work for uppercase words delimited by a space:
E.g: 'TEST CASE' => t_e_s_t_case
The following solution does not break consecutive upper case characters and is a little shorter:
const snakeCase = str =>
str &&
str
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
.map(x => x.toLowerCase())
.join('_');
However, trailing underscores after uppercase words (examples from Orwellophile as well), do not work properly.
E.g: 'TheID_' => the_i_d
Taken from https://www.w3resource.com/javascript-exercises/fundamental/javascript-fundamental-exercise-120.php.
I'm trying to capitalize every first letter of the given string, however every time I run the code I get the following error:
TypeError: val[i].charAt(...).toUpperCase(...).join is not a function
function titleCase(str) {
var strArry = str.toLowerCase().split(' ');
strArry.map(function (val) {
for (var i = 0; i < val.length; i++) {
return val[i].charAt(0).toUpperCase().join(' ') + strArry[i].splice(1);
};
});
}
titleCase("I'm a little tea pot");
String.protoype.toUpperCase() returns a string, and Array.prototype.join() is on the Array prototype, not the string.
You probably want something like this...
return val[i].charAt(0).toUpperCase() + val[i].slice(1);
...or possibly even better (at least shorter)...
function titleCase(str) {
return str.replace(/\b[a-z]/g, function(match) { return match.toUpperCase(); });
}
Here I am using a regex to match the first lowercase letter after a word boundary, and then using a custom replacer to return it uppercased.
If you supported more than latin, then use a Unicode range to select the characters.
toUpperCase returns a String and join is not a method on that prototype.
If you run through your code and put a typeof check in before mucking with the string, you can see:
function titleCase(str) {
var strArry = str.toLowerCase().split(' ');
strArry.map(function (val) {
for (var i = 0; i < val.length; i++) {
var temp = val[i].charAt(0).toUpperCase();
console.log(typeof temp);
return temp;
};
});
}
titleCase("I'm a little tea pot");
You need to join the Array after you've mapped it to produce the final combined value.
I propose this short piece of code which works well :
var titleCase = (str) => str.split(" ").map(el => el[0].toUpperCase().concat(el.substr(1))).join(" ");
console.log(titleCase("I'm a little tea pot"));
Figured it out with a bit a research and found that using val worked with me trying to use mpa()
function titleCase(str) {
var strArry = str.toLowerCase().split(' ');
var convert = strArry.map(function(val) {
return val.replace(val.charAt(0), val.charAt(0).toUpperCase());
});
return convert.join(' ');
}
titleCase("I'm a little tea pot");
Im am not Familiar with Javascript, which is why I fo not know if it makes a difference between char and string.
I am quite sure you meant do an entirely different thing, namely return a string joined from capitalized letters. What you are (trying) to do here however is returning the result of your expression (val[i].andsoonandsoforth) after the first cycle of your for loop.
The reason why the expression doesn't work will be that join(string) is usually called on arrays of strings or characters. You are calling it on a single character however!
The solution would be to first split uf the string into characters, capitalize them, store them into an array, call join on that array and return the thing afterwards.
Or just use the likely existent builtin capitalize functions on strings.
I'm looking through the lodash docs and other Stack Overflow questions - while there are several native JavaScript ways of accomplishing this task, is there a way I can convert a string to title case using purely lodash functions (or at least existing prototypal functions) so that I don't have to use a regular expression or define a new function?
e.g.
This string ShouLD be ALL in title CASe
should become
This String Should Be All In Title Case
This can be done with a small modification of startCase:
_.startCase(_.toLower(str));
console.log(_.startCase(_.toLower("This string ShouLD be ALL in title CASe")));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
_.startCase(_.camelCase(str))
For non-user-generated text, this handles more cases than the accepted answer
> startCase(camelCase('myString'))
'My String'
> startCase(camelCase('my_string'))
'My String'
> startCase(camelCase('MY_STRING'))
'My String'
> startCase(camelCase('my string'))
'My String'
> startCase(camelCase('My string'))
'My String'
with lodash version 4.
_.upperFirst(_.toLower(str))
'This string ShouLD be ALL in title CASe'
.split(' ')
.map(_.capitalize)
.join(' ');
There are mixed answers to this question.
Some are recommending using _.upperFirst while some recommending _.startCase.
Know the difference between them.
i) _.upperFirst will transform the first letter of your string, then string might be of a single word or multiple words but the only first letter of your string is transformed to uppercase.
_.upperFirst('jon doe')
output:
Jon doe
check the documentation https://lodash.com/docs/4.17.10#upperFirst
ii) _.startCase will transform the first letter of every word inside your string.
_.startCase('jon doe')
output:
Jon Doe
https://lodash.com/docs/4.17.10#startCase
That's the cleanest & most flexible implementation imo from testing it on my own use cases.
import { capitalize, map } from "lodash";
const titleCase = (str) => map(str.split(" "), capitalize).join(" ");
// titleCase("ALFRED NÚÑEZ") => "Alfred Núñez"
// titleCase("alfred núñez") => "Alfred Núñez"
// titleCase("AlFReD nÚñEZ") => "Alfred Núñez"
// titleCase("-") => "-"
From https://github.com/lodash/lodash/issues/3383#issuecomment-430586750
'JHON&JOHN C/O DR. BLah'.replace(/\w+/g, _.capitalize);
result:
'Jhon&John C/O Dr. Blah'
Here's a way using ONLY lodash methods and no builtin methods:
_.reduce(_.map(_.split("Hello everyOne IN the WOrld", " "), _.capitalize), (a, b) => a + " " + b)
This can be done with only lodash
properCase = string =>
words(string)
.map(capitalize)
.join(' ');
const proper = properCase('make this sentence propercase');
console.log(proper);
//would return 'Make This Sentence Propercase'
var s = 'This string ShouLD be ALL in title CASe';
_.map(s.split(' '), (w) => _.capitalize(w.toLowerCase())).join(' ')
Unless i missed it, lodash doesnt have its own lower/upper case methods.
const titleCase = str =>
str
.split(' ')
.map(str => {
const word = str.toLowerCase()
return word.charAt(0).toUpperCase() + word.slice(1)
})
.join(' ')
You can also split out the map function to do separate words
Below code will work perfectly:
var str = "TITLECASE";
_.startCase(str.toLowerCase());
Not as concise as #4castle's answer, but descriptive and lodash-full, nonetheless...
var basicTitleCase = _
.chain('This string ShouLD be ALL in title CASe')
.toLower()
.words()
.map(_.capitalize)
.join(' ')
.value()
console.log('Result:', basicTitleCase)
console.log('Exact Match:' , basicTitleCase === 'This String Should Be All In Title Case')
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
Here's another solution for my use case:
"devil's backbone"
Simply:
function titleCase (str) {
return _.map(str.split(' '), _.upperFirst).join(' ');
}
Using startCase would remove the apostrophe, so I had to work around that limitation. The other solutions seemed pretty convoluted. I like this as it's clean, easy to understand.
with lodash 4, you can use _.capitalize()
_.capitalize('JOHN')
It returns 'John'
See https://lodash.com/docs/4.17.5#capitalize for details
you can simply use the lowdash capitalize methode , it Converts the first character of string to upper case and the remaining to lower case.
https://lodash.com/docs/#capitalize
const str = 'titlecase this string ';
_.capitalize(str); //Titlecase This String