2
\$\begingroup\$

I have this code which does the job, however it seems to me that the same could be done with less code. And why if I put 'var newString = stored.join(' ');' below 'var stored=[];' the code doesn't work?

function titleCase(str) {

var lower = str.toLowerCase();

var arr = lower.split(' ');

var stored = [];

//takes a string from arr
for(var i = 0; i<arr.length; i++) {

 //splits and stores the string to letters 
   var word = arr[i].split(''); // ['l','i','t'...]

    // takes the first letter from the array and makes it uppercase
   for(var k=0; k<1; k++){
   var upper = word[k].toUpperCase(); 
   word.shift(); 
   word.unshift(upper);
   var joined = word.join('');
   stored.push(joined);
}

}
 var newString = stored.join(' ');

 return newString;

}

 titleCase("I'm a little tea pot");
\$\endgroup\$
2
  • \$\begingroup\$ What would be the output of titleCase("I'm a little tea pot");? \$\endgroup\$
    – Yagna Patel
    Aug 12, 2017 at 19:13
  • 3
    \$\begingroup\$ You mean it doesn't work when you move the var newString = stored.join(' '); above the loop? Well obviously that's because the array isn't yet filled. \$\endgroup\$
    – Bergi
    Aug 12, 2017 at 19:13

6 Answers 6

4
\$\begingroup\$

You can use String#replace with a regexp:

function titleCase(str) {
  return str.toLowerCase() // change the string to lowercase
    .replace(/\S+/g, function(s) { // get all non space characters sequences
      return s.charAt(0).toUpperCase() + s.substring(1); // uppercase the 1st letter and combine with the rest of the string
    });
}

console.log(titleCase("I'm a little tea pot"));

\$\endgroup\$
2
\$\begingroup\$

You can split the string by space, then use map function to iterate over words and uppercase each word and then join it back again:

"I'm a little tea pot"
  .split(' ')
  .map(function(word) {
    return word[0].toUpperCase() + word.substring(1);
  })
  .join(' ');
\$\endgroup\$
1
  • \$\begingroup\$ huh...so the argument 'word' in the function is the current value being procesed? And by 'word[0]' it takes the first letter of the word? I thought that you have to use '[ ]' only on an array. Or that value being processed becomes an array somehow? Sorry for dumb questions... \$\endgroup\$
    – Benas Lengvinas
    Aug 12, 2017 at 20:50
2
\$\begingroup\$

A more succinct string replace variant:

   str.replace (/(^|\s)\S/g, match => match.toUpperCase () );

The regexp matches the satrt of string or a space followed by a non-space character. The entrire match is up-cased and returned.

\$\endgroup\$
1
  • \$\begingroup\$ Consider using \b instead of (^|\s). Not only is it simpler, it also has the advantage of working with words that follow punctuation (such as a double-quote). \$\endgroup\$ Sep 9, 2017 at 7:31
1
\$\begingroup\$

Instead of providing a complete solution like the other answers (migrated from SO), I'll point out a few specific things that could be improved:

//splits and stores the string to letters 
var word = arr[i].split(''); // ['l','i','t'...]

That's not really necessary. You can used indexed access, a length property and the same iteration on the string itself.

// takes the first letter from the array and makes it uppercase
for(var k=0; k<1; k++){
  var upper = word[k].toUpperCase();

Why is this a loop? It loops exactly once, testing whether 0 < 1. Just drop that and place

var upper = word[0].toUpperCase();
word.shift(); 
word.unshift(upper);

Don't do that. It move the whole array around and then back. Just assign to the first index:

word[0] = upper;
var joined = word.join('');

If we didn't split the arr[i] word in the word array, we could instead use

var joined = arr[i][0].toUpperCase() // upper 
           + arr[i].slice(1); // the rest of the string
var stored = [];
//takes a string from arr
for(var i = 0; i<arr.length; i++) {
  …
  stored.push(joined);
}

Instead of filling that stored array, you could have mutated the original one (for a tiny bit of better performance). But creating a new array is fine.

However, when creating a new array with one item per item from the original array, the map method helps a lot to remove the boilerplate loop, array instantiation and pushing.

var lower = str.toLowerCase();
var arr = lower.split(' ');
…
var newString = stored.join(' ');
return newString;

Since you specifically asked about doing it with less code: use less variables. If you only use a variable exactly once, the expression might as well be inlined:

var arr = str.toLowerCase().split(' ');
…
return stored.join(' ');

It's often beneficial for clarity to use more of such descriptive variable names, but it also is a lot longer.

If you apply all these points, you'll basically end up with madox2's solution.

\$\endgroup\$
0
\$\begingroup\$

I would use the regex /([^ ]+)/g to match for each word and then change the first letter.

var titleCase = (text) => text.replace(/([^ ]+)/g, (w) => w[0].toUpperCase() + w.substring(1));

console.log(titleCase("I'm a little tea pot"));

\$\endgroup\$
0
\$\begingroup\$

I would change the regex to only match the first character in a word instead of using a concatenation like others did:

function titleCase(str) {
  return str.replace(/(^|\s)\w/g, (s) => s.toUpperCase() );
}

console.log(titleCase("I'm a little tea pot"));

However there are some questions you might want to consider. For example a newspaper would not capitalize a (or an or the) and hyphenated words are (High-Quality) are sometimes capitalized.

\$\endgroup\$
1
  • \$\begingroup\$ Use [a-z] instead of \w. \w = [a-zA-Z0-9_]. \$\endgroup\$
    – Tushar
    May 8, 2018 at 3:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy