3

My challenge is to capitalize the first letter of each word in a string while making sure all the other letters are lowercase. I have spent more hours then I'm willing to admit on this and my code is shown below. It's about 95% complete.

It's only flaw is that it returns contractions like "I'm" as "I'M". For some reason it sees contractions as two separate words. I tested this my putting a console.log immediately after the step that capitalizes the first letter (I have commented it out in the example). it returns that it is capitalizing both "I" and "M" in the same step. How do I get get it change only the "I"?

function titleCase(str) {

  str = str.toLowerCase(); //make everything lowercase
  str = str.split(" "); //make the string to array

  for(i = 0; i < str.length; i++){
    var strItem = str[i]; //take item in array
    strItem = strItem.replace(/\b./g, function(m){ return m.toUpperCase(); }); //capitalize it
   //console.log(strItem);
   str[i] = strItem; //put changed item back into array
  }

  str = str.join(" "); //turn array back into string
  return str;
}

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

Thank you for your time.

James
  • 20,957
  • 5
  • 26
  • 41
  • Quick question, but does your challenge have to use JavaScript - could you use CSS, for example: text-transform:capitalize; – Mike Sav Nov 17 '15 at 14:12

3 Answers3

2

What I suggest, is that you use <string>.charAt(<index>) to get the first letter of a string, and that <string>.slice(<index>) can give you a portion of a string.

Hint: After taking each word, you could take the first letter using charAt(), uppercase it, then take the rest slice of your string and lowercase it.

UPDATE:

Answer:

function upperFirstLetterInWords(str) {

  var totalString = "";
  //Take words from sentence - 
  //Use regex /\s+/g for many spaces in sentence.
  var words = str.split(" "); 

  //Take each word...
  //Using for loop as we have an array and it is
  //more efficient than foreach loop or lambda.
  for(var i = 0; i < words.length; ++i) {
     //Make your changes here.javascript:;
     totalString += words[i].charAt(0).toUpperCase() +
            words[i].slice(1).toLowerCase()  +
            " ";
  }

  //Remove last space.
  return totalString.trim();

}

console.log(upperFirstLetterInWords("I'm A LitTle TEa POt."));
Nick Louloudakis
  • 5,856
  • 4
  • 41
  • 54
  • 1
    Downvoting without commenting is a bad attitude. Please comment about it so I can become better. Thanks. – Nick Louloudakis Nov 17 '15 at 14:15
  • 3
    Even if it is homework, OP tried his best and couldn't do it. A full solution is just fine here. (Even though I wasn't the one who downvoted you) – Madara's Ghost Nov 17 '15 at 14:22
  • @MadaraUchiha I was wondering if I should answer it, and I preferred giving out a hint. Thank you. – Nick Louloudakis Nov 17 '15 at 14:23
  • 1
    He had a 'teaching' attitude, on my opinion that should be rewarded. – Aramil Rey Nov 17 '15 at 14:24
  • 1
    if you are not going to give a full answer, then just add a comment. – Luciano Nov 17 '15 at 14:25
  • Stack Overflow isn't just for helping the OP, it's to help all future visitors too. That is why we must provide complete working solutions when we answer. Not tips about what the OP could research and try. People shouldn't come here and have to fudge a solution together from bits of question code, and bits of answer (pseudo-)code. And regardless of homework, OP has put in a good bit of effort and just needs a bit of guidance to iron out the bugs – musefan Nov 17 '15 at 14:28
  • 1
    Thanks. Working on applying .charAt() and .slice() now. This isn't homework per-say but a freecodecamp challenge. The site seems to have a sink-or-swim approach to teaching web development. – Wes Schumaker-Reid Nov 17 '15 at 14:29
  • Added complete answer to the solution. Thank you all. – Nick Louloudakis Nov 17 '15 at 14:30
  • 2
    I suggest you test (and fix) your code, you won't get rid of the downvotes with a solution that doesn't work – musefan Nov 17 '15 at 14:32
2

Your problem seems to be that you are using a global match in your replacer expression.

Remove the g.

function titleCase(str) {
  str = str.toLowerCase();               // Make everything lowercase
  str = str.split(/\s+/);                // Make the string to array  
  for (var i = 0; i < str.length; i++) {
    var strItem = str[i];                // Take item in array
    strItem = strItem.replace(/\b./,
        function(m) {
              return m.toUpperCase();    // Capitalize it
        }
    );                           
    str[i] = strItem;                    // Put changed item back into array
  }
    
  return str.join(" ");                  // Turn array back into string
}

document.body.innerHTML = titleCase("I'm a little tea pot");

Simplified

You can create a capitalCase function and use it as the mapping (callback) function for each word.

function titleCase(str) {
  return str.split(/\s+/).map(captitalCase).join(' ');
}

function captitalCase(str) {
  return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}

document.body.innerHTML = titleCase("I'm a little tea pot");

Preserve White-Space

If you want to preserve white-space, you can replace all sequences non-white-space characters with their respective capitalCase equivalent.

function titleCase(str) {
  return str.replace(/(\S+)/g, function(m) {
    return captitalCase(m);
  });
}

function captitalCase(str) {
  return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}

document.body.innerHTML = titleCase("I'm   a   little  \n  tea   pot");
body {
  white-space: pre;
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Thank you. it worked! every regular expression I'v seen has used the g. I never thought a single letter could cause me so much frustration. – Wes Schumaker-Reid Nov 17 '15 at 14:36
  • @WesSchumaker-Reid: The `g` means it will replace **all** matches, if you omit it you only replace the first match. In your case this is fine. And I don't know you requirements but I wouldn't think you need to rely on word boundary, you could probably just use start condition: `/^./` – musefan Nov 17 '15 at 14:41
  • @WesSchumaker-Reid: I created an additional method to preserve white-space. – Mr. Polywhirl Nov 17 '15 at 14:57
  • what if the string is "I'm.a.little.tea.pot"? – korogui Nov 17 '15 at 15:05
  • @korogui: Then it would be invalid. Unless this particular condition was specified, it is outside the scope of the problem and it introduces unwanted complexity. – Mr. Polywhirl Nov 17 '15 at 15:33
0

Out of curiosity, I wanted to see if I could come up with an alternative javascript solution without using regex:

var selectedParagraph = document.getElementsByTagName('p')[1];

var textUC = [];

function toTitleCase(element) {
var textLC = element.innerHTML.toLowerCase().split(' ');
for (var i = 0; i < textLC.length; i++) {
textUC[i] = textLC[i].substr(0,1).toUpperCase() + textLC[i].substr(1);}
element.innerHTML = textUC.join(' ');
}

window.onload = toTitleCase(selectedParagraph);
<p>I'm a little tea pot</p>
<p>I'm a little tea pot</p>
Rounin
  • 27,134
  • 9
  • 83
  • 108