6
\$\begingroup\$

I'm trying to transform the following string "joe-smith" to "Joe Smith". I have the following code which seems to work okay, but is there a faster, better way?

var prettify = function(str){
   var temp = str.split('-'), i, pretty;

   for(i = 0; i < temp.length; i++){
     temp[i] = temp[i].charAt(0).toUpperCase() + temp[i].slice(1);
   }

   pretty = temp.join(' ');

   return pretty;
 };

 var prettyString = prettify('joe-smith');
// outputs "Joe Smith"
\$\endgroup\$
1
  • \$\begingroup\$ will the string passed always be in that format? (name-lastname) \$\endgroup\$
    – Erick
    Apr 13, 2015 at 1:57

5 Answers 5

10
\$\begingroup\$

Some things:

  • That pretty variable is pretty useless. Omit it.
  • temp is quite meaningless. Use a descriptive name like words or parts.
  • You could use the Array map method instead of that for loop. This will likely be slower, but more elegant:

    function prettify(str) {
        return str.split('-').map(function capitalize(part) {
            return part.charAt(0).toUpperCase() + part.slice(1);
        }).join(' ');
    }
    
  • regular expressions and the String replace method could further shorten it, and possibly improve performance:

    function prettify(str) {
        return str.replace(/(-|^)([^-]?)/g, function(_, prep, letter) {
            return (prep && ' ') + letter.toUpperCase();
        });
    }
    

    Decide yourself whether this is too cryptic or not.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ I like the map solution for code, but it sure is not the fast one as the map is rather expensive. The regex solution does capitalize any letter Mr functional programmer :) fiddle \$\endgroup\$
    – MaxZoom
    Apr 13, 2015 at 20:39
  • \$\begingroup\$ @MaxZoom: Thanks for the feedback, I was trying to be too clever. I used the non-greedy match for the case joe--smith, but a non-greedy optional in the end of a regex of course never matches anything :-/ Fixed. \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 20:57
  • \$\begingroup\$ I up-voted the answer for the regex, particularly for this (prep && ' ') \$\endgroup\$
    – MaxZoom
    Apr 13, 2015 at 21:32
  • 1
    \$\begingroup\$ …a horrible hack that I'm not proud about :-) \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 21:34
  • \$\begingroup\$ So stumbled upon this searching on how to capitalize and replace hyphens and underscores from a string. Have been trying to modify the regex to match underscores as well for a while now, but no luck. Any possibility of modifying the regex solution to replace underscores as well ? Any help highly appreciated ! \$\endgroup\$
    – Norman
    Nov 20, 2017 at 5:39
5
\$\begingroup\$

It seems there is no faster way to beautify word then your code. The only improvement could be the split method:

function prettify(str) {
  var words = str.match(/([^-]+)/g) || [];
  words.forEach(function(word, i) {
    words[i] = word[0].toUpperCase() + word.slice(1);
  });
  return words.join(' ');
}

var prettyString = prettify('joe-smith');
alert(prettyString)

\$\endgroup\$
6
  • \$\begingroup\$ Please do not use meaningless identifiers like beautify, especially on String.prototype. That operation has a quite specific name. \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 3:10
  • \$\begingroup\$ Oh, and don't use forEach like that. \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 3:10
  • \$\begingroup\$ @Bergi Why you don't like forEach? \$\endgroup\$
    – MaxZoom
    Apr 13, 2015 at 3:12
  • \$\begingroup\$ Because I'm a functional programmer and don't like side effects. If you have to use array iteration methods, there is a much better suited one. \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 3:20
  • \$\begingroup\$ This approach seems to be more fault proof. See Fiddle \$\endgroup\$
    – MaxZoom
    Apr 13, 2015 at 21:48
1
\$\begingroup\$

You may be over thinking this. You can use the JavaScript replace method which take a regular expression (first parameter) and then what you want to replace it with (second parameter). then return the new string.

var prettify = function(str){      
    var prettyStr = str.replace(/-/, ' ');
    return prettyStr ;
};

Here is a revision that sets the first and last name to uppercase.

var prettify = function(str){      
    var splitStr = str.replace(/-/, ' ');
    var prettyStr = splitStr.replace( /\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();} );
    return prettyStr ;
};

The to uppercase uses the same replace but it grabs on name at a time then runs a function that takes the text capitalizes the first letter ( charAt(0) the first character in the name ) then attaches the rest of the sub-string ( substr(1) starts at the second character ).

I got the code reference from this post. https://stackoverflow.com/questions/4878756/javascript-how-to-capitalize-first-letter-of-each-word-like-a-2-word-city

\$\endgroup\$
1
  • \$\begingroup\$ op wants capitalization as well \$\endgroup\$
    – Logan Murphy
    Apr 13, 2015 at 2:30
1
\$\begingroup\$

You would have to make a miniature finite state machine like this one

function prettify(input) {
    var output = "", shouldCap = 1, c;
    for(var i = 0; i < input.length; i++) {
        c = input[i];
        if(c == "-") {
            output += " ";
            shouldCap = 1;
        } else if(shouldCap) {
            output += c.toUpperCase();
            shouldCap = 0;
        } else {
            output += c;
        }
    }    
    return output;
}

alert(prettify("logan-murphy"));

Also minifying your code can increase the speed since javascript is executed on the fly. I provided benchmarks of my code vs the accepted answers code. Look at the console's log to confirm the speed difference. Note that this was only tested in Chrome.

var timer = (function() {
    var last;
    return function() {
        var now = new Date().getTime();
        if(last) {
            console.log(now - last);
            last = 0;
        } else {
            last = now;
        }
    };
}()), prettify;

function doTests() {
    timer();
    for(var i = 0; i < 100000; i++) {
        prettify("logan-murphy");        
    }
    timer();
}

//accepted answer
prettify = function(str) {
    return str.split('-').map(function capitalize(part) {
        return part.charAt(0).toUpperCase() + part.slice(1);
    }).join(' ');
};

doTests();

//my answer
prettify = function(input) {
    var output = "", shouldCap = 1, c;
    for(var i = 0; i < input.length; i++) {
        c = input[i];
        if(c == "-") {
            output += " ";
            shouldCap = 1;
        } else if(shouldCap) {
            output += c.toUpperCase();
            shouldCap = 0;
        } else {
            output += c;
        }
    }    
    return output;
};

doTests();

//using arrays - still slower
prettify = function(input) {
    var output = new Array(input.length), shouldCap = 1, c;
    for(var i = 0; i < input.length; i++) {
        c = input[i];
        if(c == "-") {
            output[i] = " ";
            shouldCap = 1;
        } else if(shouldCap) {
            output[i] = c.toUpperCase();
            shouldCap = 0;
        } else {
            output[i] = c;
        }
    }    
    return output.join("");
};

doTests();

\$\endgroup\$
7
  • \$\begingroup\$ "have to make" - for what? For better, more readable code? \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 3:26
  • \$\begingroup\$ Is shouldCap a boolean or not? \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 3:28
  • \$\begingroup\$ i would say its faster since it doesnt depend on regexp or any algorithm that requires an iteration over the string. also 1 and 0 are the smallest truthy/falsy values available so it doesnt have to be a boolean. \$\endgroup\$
    – Logan Murphy
    Apr 13, 2015 at 3:59
  • \$\begingroup\$ What is for (var i=0; i<input.length; i++) doing if not an iteraton over the string? And you are doing shouldCap = true, which is a boolean. Changing the type of the variable will likely need more memory. \$\endgroup\$
    – Bergi
    Apr 13, 2015 at 14:34
  • \$\begingroup\$ @Bergi i know, but it only requires one iteration, vs other solutions that use match, split, join replace, slice which have multiple iterations \$\endgroup\$
    – Logan Murphy
    Apr 13, 2015 at 15:06
0
\$\begingroup\$

The Array.map() function helps one to do this in much simpler way.

For Example: share-information.

Output will be Share-Information.

capitalize (str) { return (str) ?str.split('-')
.map(text=> text.charAt(0).toUpperCase()+ word.slice(1))
.join('-') :str.charAt(0).toUpperCase() + str.slice(1);  }

You can use Regex value in split for multiple symbols.

for eg: .split(/,-_| /)

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Welcome to Code review and thank you for contributing an answer. Bergi's answer already mentioned using .map as well as regular expressions. Please read How do I write a good answer?: "Read the other answers so you don't cover duplicate points. While it is fine to mention points in multiple answers, each answer should contain at least one novel observation." \$\endgroup\$ Aug 31, 2018 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.