Jonatas Walker
Jonatas Walker

Reputation: 14168

Unexpected string.replace() result

Why does this fail? Should I (somehow) escape letter i — this doesn't fail with other characters.

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

// Prints:   "i'm A Little Tea Pot"
// Expected: "I'm A Little Tea Pot"

function titleCase(str) {
  return str.split(' ').map(function(each) {
    return each.toLowerCase().replace(each[0], each[0].toUpperCase());
  }).join(' ');
}

Upvotes: 3

Views: 255

Answers (7)

Useless Code
Useless Code

Reputation: 12422

toLowerCase returns a new string, it does not update the value of each. So when "I'm" is assigned to each what you are effectively getting is:

"i'm".replace("I", "I".toUpperCase());

"I" is not in the string "i'm", so nothing is replaced.

Instead of splitting the string, running replace on each token and joining it, I would instead do it with a single replace with RegExp and a replacement function:

function titleCase (str) {
  var upper = function (wholeMatch, before, firstLetter) {
      return before + firstLetter.toUpperCase();
    };
  return (str.toLowerCase()).replace(/(^\s*|\s+)(.)/g, upper);
}

titleCase("I'm a little tEa pot");
// "I'm A Little Tea Pot"

titleCase("   I'm a little tEa pot");
// "   I'm A Little Tea Pot"

titleCase(" I'm a little    tEa pot");
" I'm A Little    Tea Pot"

/(^\s*|\s+)(.)/g finds either the start of the string (with optional white-space) or white-space followed by any single character. The upper function then uppercases the character and returns any captured white-space and that upper case character to act as the replacement.

Upvotes: 0

Hin Fan Chan
Hin Fan Chan

Reputation: 1653

Run the following snippet and look at the output.

var each ="I'm";

console.log(`Change ${each[0]} to ${each[0].toUpperCase()} in ${each.toLowerCase()}`);
console.log('Result:', each.toLowerCase().replace(each[0], each[0].toUpperCase()));

You should see that the char to search is a capital "I" but the first char in the string is a lower case "i".

function titleCase(str) {
  return str.toLowerCase().split(' ').map(function(each) {
    return each.replace(each[0], each[0].toUpperCase());
  }).join(' ');
}

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

// Prints:   "i'm A Little Tea Pot"
// Expected: "I'm A Little Tea Pot"

Upvotes: 1

Rahul Patel
Rahul Patel

Reputation: 5246

For "I'm" part of string the uppercase "I" converted to lowercase "i" and trying to replace the lowercase "i" with uppercase 'I' which is not even exist in the string.

So modified your function in the beginning converted entire string in lowercase.

Please check below working snippet.

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

// Prints:   "i'm A Little Tea Pot"
// Expected: "I'm A Little Tea Pot"

function titleCase(str) {
  str = str.toLowerCase();
  return str.split(' ').map(function(each) {
    //console.log(each[0].toUpperCase());
    return each.toLowerCase().replace(each[0], each[0].toUpperCase());
  }).join(' ');
}

Upvotes: 1

Deepak Sharma
Deepak Sharma

Reputation: 419

You can also use regular expression for this.

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

Upvotes: 0

newfurniturey
newfurniturey

Reputation: 38456

The .toLowerCase() is happening on the original value of each, so the .replace() may/will not give you the expected results.

If you split it out as a separate statement, it should work as expected:

function titleCase(str) {
    return str.split(' ').map(function(each) {
        each = each.toLowerCase();
        return each.replace(each[0], each[0].toUpperCase());
    }).join(' ');
}

As a slightly more efficient approach, it would also be better to move the .toLowerCase() to before the .map() call so it only needs to be called once, instead of once-per-word:

function titleCase(str) {
    str = str.toLowerCase();
    return str.split(' ').map(function(each) {
        return each.replace(each[0], each[0].toUpperCase());
    }).join(' ');
}

Upvotes: 3

Rajesh
Rajesh

Reputation: 24945

You can try something like this:

Array.map

function titleCase(str) {  
  return str.split(' ').map(function(each) {
    return each.charAt(0).toUpperCase() + each.substring(1).toLowerCase();
  }).join(' ');
}

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

Array.reduce

function titleCase(str) {  
  return str.split(' ').reduce(function(p,c) {
    return p + " " + toTitle(c);
  });
}

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

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

Upvotes: 1

gurvinder372
gurvinder372

Reputation: 68413

It is simple.

replace(each[0], each[0].toUpperCase());

will only replace lowerCase items, and I is not lowercase since each[0] being not yet replaced.

For other words in the sentence, their first character is lowercase, so they stay lowercase after lowercase() is called on each.

Upvotes: 0

Related Questions