1
\$\begingroup\$

Problem

Write a function that accepts a string, capitalizes the first letter of each word in the string, and returns the capitalized string.

Examples

function('a short sentence') => 'A Short Sentence'
function('a lazy fox') => 'A Lazy Fox'
function('look, it is working!') => 'Look, It Is Working!'

Code

I've solved the above problem using a few methods. If you'd like to review the codes and provide any change/improvement recommendations please do so, and I'd really appreciate that.

Python

def capitalize_built_in_title(sentence: str) -> str:
    """Capitalizes the first letters and preserves the spaces"""

    return sentence.title()


def capitalize_naive_one(sentence: str) -> str:
    """Capitalizes the first letters and doesn't preserve the spaces"""

    words = sentence.split()

    for index_word, word in enumerate(words):
        word_list = split_char_by_char_list(word)
        for index_letter, letter in enumerate(word_list):
            if index_letter == 0:
                word_list[index_letter] = letter.upper()
                word = "".join(word_list)
                break
        words[index_word] = word

    return " ".join(words)


def capitalize_naive_two(sentence: str) -> str:
    """Capitalizes the first letters and doesn't preserve the spaces"""

    words = sentence.split()

    for index_word, word in enumerate(words):
        words[index_word] = word[:1].upper() + word[1:]

    return " ".join(words)


def capitalize_naive_three(sentence: str) -> str:
    """Capitalizes the first letters and preserves the spaces"""

    import regex as re
    split_on_first_letter = list(
        re.splititer(r'(?i)(?<=^|\s)([a-z])', sentence))
    for index, item in enumerate(split_on_first_letter):
        if item is not None and len(item) == 1:
            split_on_first_letter[index] = item.upper()
    return "".join(split_on_first_letter)


def split_char_by_char_re(string: str):
    import re
    return re.findall(r'.', string)


def split_char_by_char_regex(string: str):
    import regex as re
    return re.findall(r'.', string)


def split_char_by_char_list(string: str):
    return list(string)


if __name__ == '__main__':
    # ---------------------------- TEST ---------------------------
    DIVIDER_DASH_LINE = '-' * 50
    GREEN_APPLE = '\U0001F34F'
    RED_APPLE = '\U0001F34E'

    test_sentences = ['hey    there,    how was    your day?',
                      ' good   day! ', '  it waS GreaT!']
    capitalized_sentences = [
        'Hey    There,    How Was    Your Day?', ' Good   Day! ', '  It WaS GreaT!']

    # ---------------------- DONT REPEAT YOURSELF  -------------------------------
    for index, sentence in enumerate(test_sentences):
        print(DIVIDER_DASH_LINE)
        if capitalized_sentences[index] == capitalize_built_in_title(sentence):
            print(f'{GREEN_APPLE} "{sentence}" =>  "{capitalize_built_in_title(sentence)}"')
        else:
            print(f'{RED_APPLE} "{sentence}" =>  "{capitalize_built_in_title(sentence)}"')

        if capitalized_sentences[index] == capitalize_naive_one(sentence):
            print(f'{GREEN_APPLE} "{sentence}" =>  "{capitalize_naive_one(sentence)}"')
        else:
            print(f'{RED_APPLE} "{sentence}" =>  "{capitalize_naive_one(sentence)}"')

        if capitalized_sentences[index] == capitalize_naive_two(sentence):
            print(f'{GREEN_APPLE} "{sentence}" =>  "{capitalize_naive_two(sentence)}"')
        else:
            print(f'{RED_APPLE} "{sentence}" =>  "{capitalize_naive_two(sentence)}"')

        if capitalized_sentences[index] == capitalize_naive_three(sentence):
            print(f'{GREEN_APPLE} "{sentence}" =>  "{capitalize_naive_three(sentence)}"')
        else:
            print(f'{RED_APPLE} "{sentence}" =>  "{capitalize_naive_three(sentence)}"')

Output

--------------------------------------------------
🍏 "hey    there,    how was    your day?" =>  "Hey    There,    How Was    Your Day?"
🍎 "hey    there,    how was    your day?" =>  "Hey There, How Was Your Day?"
🍎 "hey    there,    how was    your day?" =>  "Hey There, How Was Your Day?"
🍏 "hey    there,    how was    your day?" =>  "Hey    There,    How Was    Your Day?"
--------------------------------------------------
🍏 " good   day! " =>  " Good   Day! "
🍎 " good   day! " =>  "Good Day!"
🍎 " good   day! " =>  "Good Day!"
🍏 " good   day! " =>  " Good   Day! "
--------------------------------------------------
🍎 "  it waS GreaT!" =>  "  It Was Great!"
🍎 "  it waS GreaT!" =>  "It WaS GreaT!"
🍎 "  it waS GreaT!" =>  "It WaS GreaT!"
🍏 "  it waS GreaT!" =>  "  It WaS GreaT!"

JavaScript

// --- Problem
// Write a function that accepts a string.  The function should
// capitalize the first letter of each word in the string then
// return the capitalized string.
// --- Examples
//   function('a short sentence') => 'A Short Sentence'
//   function('a lazy fox') => 'A Lazy Fox'
//   function('look, it is working!') => 'Look, It Is Working!'

function capitalize_naive_one(sentence) {
	words = [];
	for (let word of sentence.split(' ')) {
		if (word != '') {
			word = (word[0].toUpperCase() + word.slice(1));
		}
		words.push(word);
	}
	return words.join(' ');
}

function capitalize_naive_two(sentence) {
	capitalized_sentence = sentence[0].toUpperCase();
	for (let i = 1; i < sentence.length; i++) {
		if (sentence[i - 1] === ' ') {
			capitalized_sentence += sentence[i].toUpperCase();
		} else {
			capitalized_sentence += sentence[i];
		}
	}

	return capitalized_sentence;
}

test_sentences = ['hey    there,    how was    your day?', ' good   day! ', '  it waS GreaT!'];
capitalized_sentences = ['Hey    There,    How Was    Your Day?', ' Good   Day! ', '  It WaS GreaT!'];

count = 0;
for (let sentence of test_sentences) {
	if (capitalized_sentences[count] === capitalize_naive_one(sentence) && capitalized_sentences[count] === capitalize_naive_two(sentence)) {
		console.log('🍏 "'.concat(sentence, '" => "', capitalized_sentences[count], '"'));
	}
	count++;
}

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You are not just capitalizing the first letter of each word, you have different variations where the same phrase gives both "red" and "green" output. Just "capitalizing the first letter of each word" is relatively simple. \$\endgroup\$ Oct 17, 2019 at 16:41
  • 1
    \$\begingroup\$ I can cover all your "green" cases with single function (with retaining all spaces and without redundant lowercasing). If that suits your requirement? \$\endgroup\$ Oct 17, 2019 at 17:03

1 Answer 1

1
\$\begingroup\$

JavaScript

  • Javascript uses camelCase by convention not snake_case
  • Undeclared variables are placed in global scope or throw a parsing error in strict mode. The further a variable's scope is from the current scope the longer it takes to get the reference and thus the slower the code. You have not declared words, capitalized_sentence and more in the testing code
  • Best to use constants for variables that do not change.
  • Always use strict equality === and inequality !== as they are faster and unless you are familiar with JS type coercion safer to use as they do not coerce type.

Rewrite

Rewriting your function using idiomatic JS

function capitalize(str) {
    const words = [];
    for (const word of str.split(" ")) {
        if (word !== '') { word = word[0].toUpperCase() + word.slice(1) }
        words.push(word);
    }
    return words.join(" ");
}

You could also do a one liner using string replace and a RegExp

const capitalize = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());

However the regular expression does make it a little slow.

Immutable strings

JavaScript string are immutable, and thus you must take great care when handling strings as allocation overheads can slow things down. Avoid needless copying of strings

Accessing characters

The quickest way to get at characters in a string is via String.charCodeAt(idx)

For simple capitalization if you know that the char is lower case String.fromCharCode(str.charCodeAt(0) - 32) is twice as fast as str[0].toUpperCase()

With the above two points in mind you can double (or better as it skips already uppercase words) the performance with the following.

  • Only copies words if they need capitalization.
  • Uses the character code to do the capitalization.
  • Uses the one array, to avoid overhead building a second array.

.

function capit(str) {
    const words = str.split(" ");
    var i = 0, char;
    for (const word of words) {
        word !== "" && (char = word.charCodeAt(0)) > 96 && char < 122 && 
            (words[i] = String.fromCharCode(char - 32)  + word.slice(1));
        i++;
    }
    return words.join(" ");
}
\$\endgroup\$
0

Your Answer

By clicking β€œPost Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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