Title Case a Sentence - I HAVE CREATED A MONSTER (but it works!)

Hi all!

It works, but it has 6 variables and 3 FOR loops… GROSS. Please help!

Is there is some concept that I am missing that would make it easier?
How can I refine this? Thanks in advance everyone!!

I’ve left my “strategy comments” in for clarity.

function titleCase(str) {
  //1. make whole string lower case
  //2. break the string into array of words
  //3. make an array of capitalized first letters of each word
  //4. make another array of the rest of each word
  // join these two arrays together, element by element
  
  var words = str.toLowerCase().split(' ');
  var wordCaps = [];
  var tail ='';
  var wordEnds = [];
  var capitalized = [];
  
  for (var i=0; i<words.length; i++) {
    wordCaps.push(words[i][0].toUpperCase());
    tail='';
    for (var j=1; j<words[i].length; j++) {
      tail += words[i][j]; 
    }
    wordEnds.push(tail);
  }
  
  for (z=0; z<words.length; z++) {
    capitalized.push(wordCaps[z]+wordEnds[z]);
  }
  var final = capitalized.join(" ");
  return final;
}
1 Like

Goodness gracious. Can anybody swing me some tips on how to format code in this forum also please?

1 Like

Just copy your code, select it and click this (</>) button.

1 Like

Hi. :slight_smile:

.slice() is your friend. You can replace this

    tail='';
    for (var j=1; j<words[i].length; j++) {
      tail += words[i][j]; 
    }

with

tail = words[i].slice(1);
// this assigns to `tail` all chars from `words[i]` except the first

You can even go with

  for (var i=0; i<words.length; i++) {
    wordCaps.push(words[i][0].toUpperCase());
    wordEnds.push(words[i].slice(1));
  }
  // with this approach you can eliminate the `tail` variable.

You can go as far as going with just one for-loop by pushing to capitalized directly.

  for (var i=0; i<words.length; i++) {
    capitalized.push(words[i][0].toUpperCase() + words[i].slice(1));
  }
  // with this you can eliminate both `wordCaps` and `wordEnds`.

You can even go as far as eliminating loops entirely by using .map, but I’ll let you figure it out on your own :wink: .


So in the end the code now looks like

function titleCase(str) {
  var words = str.toLowerCase().split(' ');
  var capitalized = [];
  
  // using `let` instead of `var` is recommended in loops,
  // but that's ES6.
  for (var i=0; i < words.length; i++) {
    capitalized.push(words[i][0].toUpperCase() + words[i].slice(1));
  }
  
  var final = capitalized.join(" ");
  return final;
}

LINKS
String.prototype.slice()
Array.prototype.map()

6 Likes

You have the right idea. That’s exactly what you need to do, it’s just that you are unnecessarily complicating the execution.

There is no need to remember the first letter, then remember the rest of the word and then use another loop to add these two parts together. You can do it in one loop. Instead of 3 arrays you can use just one to store the whole capitalized word.

Get familiar with javascript array and string methods and prototypes. All of this:
tail=’’;
for (var j=1; j<words[i].length; j++) {
tail += words[i][j];
}

Is equivalent to: tail=words[i].substring(1);
To loop through the whole array you can use map (Array.prototype.map())

Hope this helps :slight_smile:

2 Likes

Thanks so much @kevcomedia and @Beekey !

The elegance! Thanks so much!

I have a question. Is there any trade off with regards to load time or that sort of thing when using in-built javascript methods like .slice() vs using manual loops?

And what is the chance of an interview question coming up with the caveat ‘do not use any js methods’?

My JS knowledge is not deep enough to answer this, but typically using built-in methods is better that manual loops, because more code (as is the case in loops) means more chances for bugs to creep in.

I have no work experience yet, so I can’t answer. :sweat_smile:

1 Like

I have created a mess - Help me to simplify.

var s = str.toLowerCase();
//Split a given string into array of word.
var arrayWord = s.split(" ");
var arChar = [];
var newStr= “”;
var newAr = [];

str="";
//Split it down further
for (var i =0 ; i< arrayWord.length ;i++){
arChar[i] = arrayWord[i].split("");
}

for(var j =0 ;j < arChar.length ;j++) {
newAr = arChar[j];
for (var k = 0 ; k <=0 ; k++ ) {
newAr[k]= newAr[k].toUpperCase();
arChar[j] = newAr.join("");
}
}
str = arChar.join(" ");
return str;

I only just finished this algorithm challenge myself, but I would suggest combining a few methods.
For example, you could start with var s = str.toLowerCase().split(’ '); Now you have an array of lowercase words.

I think it’s neat that you found a functional monster, and somebody with more experience can
probably better understand why you used arChar and newAr, but I don’t see newStr getting used.
One for loop should suffice, though you may have to combine methods again.

I used .map() instead to take each words first letter i.e. val[0] , capitalize it, combine it with the rest of the word (using slice(), and return it to the new var. i.e. var new = s.map(function(val) { //write code here }); Then returned new.join(); for output.

If you just want to tidy up your for loops you could maybe nest them like we did here : https://www.freecodecamp.com/challenges/nesting-for-loops

@Iarflaith You’re there are unnecessary line of codes that don’t need to be there after i read your input and look at the code again, such as reinitialize str= “”;
I am a newbee I can’t quite wrap my head around some build-in function such as map() , reduce(); which i should spend more time on them, Thanks for your feedback anyway. :slight_smile:

I like that you comment your steps. I started doing the same and really helps me follow my own reasoning when I go back and review my code. You can combine all your multiple steps in a shorter version:
function titleCase(str)
{
var arr = str.split(" "); //output: [“this”, “is”, “the”, “provided”, “string”]

     	/*1.For every word, which has now become an element of the array, so I can call it arr[i], capitalize the first letter. This is done by charAt(0).UpperCase(). For ex. arr[0].charAt(0).toUpperCase() will output T. 
        2. Now you want to add his to T, which is the substring obtained from index 1 thru the end of this = arr[0]: this is done by concatenating with arr[0].substring(1, arr[0].length).
        3. Now you need to make sure that your substring is lower case, that is why you add .toLowerCase()
        4. You want this process to be done for every word: this, is, the, provided, string. So you loop it starting from i = 0 to i < arr.length
        5. Remember to store your new value at every cycle in a variable arr[i.]
        6. At this point you can return you variable arr, but you want a string now an array. So you join and return arr.join(" ")
        
        
        */
     	for(var i = 0; i < arr.length; i++)

     		arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1, arr[i].length).toLowerCase();
     		
     	return arr.join(" ");
     	
     }
    titleCase("this is the provided string");
1 Like