# Capitalizing the first letter of every word in a string (with arbitrary spacing)

I solved the following question

You are asked to ensure that the first and last names of people begin with a capital letter in their passports. For example, alison heck should be capitalised correctly as Alison Heck.

Given a full name, your task is to capitalize the name appropriately.

Input Format

A single line of input containing the full name, S.

Constraints

• 0 < len(S) < 1000
• The string consists of alphanumeric characters and spaces.

Note: in a word only the first character is capitalized.

Example 12abc when capitalized remains 12abc.

Output Format:

Print the capitalized string, S.

Here's my code:

#!/bin/python

import math
import os
import random
import re
import sys

# Complete the solve function below.
def solve(s):
ls = s.split(" ")
new_word = ""
new_name = ""
for word in ls:
if len(word) > 1:
new_word = new_word + word[0].title() + word[1:] + " "
else:
new_word = new_word + word.title() + " "
new_name = new_name.join(new_word)
return new_name

if __name__ == '__main__':
fptr = open(os.environ['OUTPUT_PATH'], 'w')

s = raw_input()

result = solve(s)

fptr.write(result + '\n')

fptr.close()


I think I used a very long approach and would appreciate more efficient techniques to do the same.

The primary problem which I faced is handling arbitrary spaces and hence I went with this approach but the code smells bad. One things I can think of is reducing variables but that wouldn't be very helpful and make it less readable I think

EDIT : The question is this question from HackerRank however the solution to this problem on the website does not include test cases where capitalize() and title() fail. The failing of these and more methods have also been discussed here

• provide links to original problems for such challenges Nov 22 '20 at 18:28
• okk will do @hjpotter92 Nov 22 '20 at 18:45

• remove the unused imports

Your code logic is OK but the execution is not good. Try not to overcomplicate it. Take a piece of paper and write the procedure of how a human like YOU would do it, just with a pencil and paper.

read the names individually... make first character capital if it isn't a digit


Now that you have basic design, become more specific, or in Python terms

• read the names individually for word in string.split()
• make the first character capital: string.title()
• if it isn't a digit : if not string[0].isdigit()

The primary problem which I faced is handling arbitrary spaces

string.split() will return the same thing, let it be 1 space or 1000 spaces. It does not matter

Now you have exactly what you need, it is just a matter of putting it together.

for every word in words, capitalize if the first character isn't a digit else do nothing

    return " ".join(word.title() if not word[0].isdigit() else word for word in words.split())


Furthermore, using capitalize() will avoid the extra check

    return " ".join(word.capitalize() for word in words.split(' '))


EDIT:

You have to use .split(' ') and NOT .split() since .split() removes all the whitespaces.

As you mentioned, title() and capitalize() fail for scenarios where you pass something like

ALLISON heck


Output

Allison Heck


In that case, you need to have extra checks. The best thing to do here is to create another function that specifically capitalizes the first letter

Here is what I thought of

def cap_first(word):
return word[:1].upper() + word[1:]


the solve function remains the same

def solve(words):
return ' '.join(cap_first(word) for word in words.split(' ')


## Benchmarks

the latter code is surely more readable and compact, but what is its performance compared to the previous solution?

I will measure the execution time in the following manner using the time module

for iterations in (10 ** 5,11 ** 5, 10 ** 6, 11 ** 6):
print(f"\n{iterations} iteartions\n")

start = time.time()
for _ in range(iterations): solvenew(names)
print(f"Time taken for new function: {time.time() - start:.3f} s")

start = time.time()
for _ in range(iterations): solveoriginal(names)
print(f"Time taken for original function: {time.time() - start:.3f} s")


Here are the results

#                      Time taken
#
#     iterations  |  original    |   new
#   --------------------------------------
#       10 ** 6   |   2.553 s    |  2.106 s
#   --------------------------------------
#       11 ** 6   |   6.203 s    |  5.542 s
#   --------------------------------------
#       10 ** 7   |   32.412 s   |  24.774 s


Feel free to try it yourself

• Hey, thanks for the answer I've used s.split(" ") instead of s.split() which would give different results like take the string 'hey ooo hehehe jeje' the first one gives the result as ['hey', 'ooo', 'hehehe', '', '', '', 'jeje'] while the second gives the result as ['hey', 'ooo', 'hehehe', 'jeje']. I did this so that when I use join the spaces are taken care of else they vanish however I can't understand why use used split()? btw the import files were imported by the website itself I just wrote the function Nov 22 '20 at 18:53
• okk I also just checked I had made a mistake the code works fine Nov 22 '20 at 18:57
• I noticed another error with capitalize which I had read about earlier on stackoverflow as well when you use capitalize and say the word is UK it changes it to Uk which is not what we want same with title it also does not handle ' well Nov 22 '20 at 18:59
• yes it did but it's because they didn't consider those cases I read about it here stackoverflow.com/questions/1549641/… Nov 22 '20 at 19:00
• Yeah i guess but I'm more concerned about finding the best way to avoid any errors in any sore of case. Should I just remove the hackerrank portion and add that as a footnote instead of the main premise then? Nov 22 '20 at 19:02