Create a new array and modify elements on an offset index

Tell us what’s happening:

I’m trying to create a new array and check if the element is a space character. And then if it, I want to change the element offset by 1 in the index to change to upper case. I feel like the logic makes sense, but I believe I am making mistakes with the .map method. Can someone please shed some light on what I need to correct with the str.map function?

Your code so far


function titleCase(str) {
str = str.split('')
let newStr = str.map(function(element, index){
  if(element == ' '){
    str.slice((index+1), 1, str[index+1].toUpperCase())
  } else {
    return element.toLowerCase();
  }
})
console.log(newStr)
}

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

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0.

Challenge: Title Case a Sentence

Link to the challenge:

I suggest that instead of making an array of all the characters with split(''), that you instead make an array of all the words with split(' '). Then you can use map on that words array to convert each word to title case, and then return that array joined back together with spaces via join(' ').

Generally when looping, I would avoid making any mutations to the element being looped over.

Ok, so I think I understand what you are saying. I reworked the array to separate each word and not by character. I thought using the .charAt() would be a good way to check if the character was the first in the string, but it seems to have an issue.
This code:

if(element.charAt(index) == 0){
      return element.toUpperCase();
    } else {
    return element
}
  })

gives me something like this:

[ 'I\'m', 'A', 'little', 'TEA', 'POT' ]

Do you think I should just abandon the map function and create a for loop instead? I’m not sure I am using this effectively.

I think you’re misunderstanding how map works. It takes a function that will receive each element in the array, and that function needs to return a value (generally transformed in some way).

I think you should keep the map, you just need to think of a function that takes a word as input, and returns that word in title case.

So we’d want this function to work this way:

toTitleCase("i'm") // "I'm"
toTitleCase('cHaRs') // 'Chars'
toTitleCase('little') //'Little'

Let’s build it in pseudo code:

function toTitleCase(word) {
  const first = // get first letter in word
  const rest = // get all characters in word after the first
  return first.toUpperCase() + rest.toLowerCase();
}

Then your overall function could simply look like this

str.split(' ').map(toTitleCase).join(' ');

Here is what I managed to come up with:

function titleCase(str) {
  str = str.split(' ') 
  let newStr = str.map(function(element, index){
    let first = element.charAt(0).toUpperCase();
    let strRemainder = element.substr(1).toLowerCase();
    return first + strRemainder
  })
  newStr = newStr.join(' ')
  return newStr
}

It passed all of the FCC tests, but I want to make it shorter if I can. I’m interested in the last line of code you wrote, but I don’t fully understand how it would work. I’ll play around with it some more and try to make that one-liner. Thanks for all your help

Think about how you can use index here instead of the charAt method.

Why not just return the joined version of newStr in one step?

On a separate note, to make the code more readable and to avoid mutating the original argument values passed to the function, you should use variable names that describe the values.

For example, the following

  str = str.split(' ') 

becomes

  const words = str.split(' ') 

and

  let newStr = str.map(function(element, index){

becomes

  const capitalizedWords = words.map(function(word, index){

and

    let first = element.charAt(0).toUpperCase();

becomes

    const firstLetter = word[0].toUpperCase();

and

let strRemainder = element.substr(1).toLowerCase();

becomes

let wordRemainder = word.substr(1).toLowerCase();

and

    return first + strRemainder

becomes

    return firstLetter + wordRemainder

and finally

  newStr = newStr.join(' ')
  return newStr

becomes

  return capitalizedWords.join(' ')
2 Likes

Can you tell me a little more about what index vs charAt serves as? I cannot find the method called index, only indexOf. Is that the method you are referring to?

Never mind, you are talking about using index in the function. Sorry.

I made all of those variable changes you suggested, thanks. Here is what I got when using the index and not charAt() method:

const firstLetter = word[index=0].toUpperCase();

Simply word[0] will get you the first character of the string word.

Do you feel that you got a better grasp of map now?

I’m sorry I suggested some major changes to your original algorithm, I feel I overstepped the bounds a bit…

Going off of what @RandellDawson said, there’s also a case to be made that you shouldn’t even make variables at all. Using the same logic, your result could look like this:

function titleCase(str) {
  return str
    .split(' ') 
    .map(function(word) {
      const firstLetter = word[0].toUpperCase();
      const wordRemainder = word.substr(1).toLowerCase();
      return firstLetter + wordRemainder;
    })
    .join(' ');
}

Where you chain together methods one after the other.

str
  .split(' ') // input string => array of words
  .map(...)   // array of words => array of title case words
  .join(' ')  // array title case words => output string

That said, there’s nothing wrong with making new variables at each step, but I think that with just the method names there is enough semantic information for the code to be easily understood.