4

I've been trying to write a function which converts under_score_words to camelCaseWords. The following is how I'd do something like this in the past;

functionName = "function_for_test_case"
for character in functionName:
    if character == "_":
        functionName = functionName.replace("_" + functionName[functionName.index(character) + 1], functionName[functionName.index(character) + 1].upper())

print functionName

which correctly outputs:

functionForTestCase

However this time I originally tried doing it another way, which I found a bit neater:

functionName = "function_for_test_case"
for index, character in enumerate(functionName):
    if character == "_":
        functionName = functionName.replace("_" + functionName[index + 1], functionName[index + 1].upper())

print functionName

Which instead outputs:

functionFor_test_case

I was stumped to why it wasn't working... I figured it might've been since I was changing the length of the string (by removing the underscore), but then I'm not sure why the first method works.

Also, if you print the replace as it goes for the second function, you can see it does actually find and replace the rest of the values, but ofcourse it does not save them. For example:

functionName = "function_for_test_case"
for index, character in enumerate(functionName):
    if character == "_":
        print functionName.replace("_" + functionName[index + 1], functionName[index + 1].upper())


functionFor_test_case
function_forTest_case
function_for_testCase

From what I could tell, these functions were essentially doing the same thing in different wording, could anyone explain why they have a different output?

Edit: I'ved edited the for loops to make it more obvious of what I was trying

  • You are changing the location of the following characters. In the first example, you then hunt for the character you want again, while in the second the value of i is wrong so the replace call doesn't find, for example "_t". – Guy Feb 05 '18 at 00:10

2 Answers2

3

enumerate(functionName) relies on the original string. The first time you replace 2 chars with just 1 (_f -> F), the indices become invalid. So at some point you have this situation:

index == 12
character == '_'
functionName == 'functionFor_test_case'
functionName[index + 1] == 'e'

So you try to replace _e with E and it's simply not there.

BTW, take a look at camelize() function in inflection library.

kszl
  • 1,203
  • 1
  • 11
  • 18
  • 1
    Any explanation why the first function *does* work? It should have the same problem, even though the index is hidden there. – Jongware Feb 05 '18 at 00:25
  • You have searched through the edited string for the location of '_' , and change that and the next character, so you are working on the edited string. Your iteration isn't really necessary, it just tells you how many underscores there are. – Guy Feb 05 '18 at 00:29
  • As opposed to the second solution it doesn't rely on the original string. functionName.index(character) checks the CURRENT index of the character. – kszl Feb 05 '18 at 00:31
  • 1
    @usr2564301, I think it's becasue the index is found at the time when the character is found, rather than there being a preset index for each character in the string. –  Feb 05 '18 at 00:32
  • @Guy: ah – the replace in there is only called `functionName.count('_')` times :) and there is no need to check *every* character. A plain loop over this `count` value would do exactly the same. – Jongware Feb 05 '18 at 00:35
0

You also don't need to do the iteration, as you already know when you will make a change, it's at every point that index("_") doesn't fail, so just loop on that working. All your iteration is doing is counting the underscores. The bit of your code that is working is

# you have already assessed that the character is '_' by the if
i = functionName.find('_')
# your iteration had found another underscore, but by using index() here
# you have ignored anything else, so is equivalent to just checking the
# return of index() or find()
while i >= 0:
    functionName = functionName.replace(
        "_" + functionName[functionName.index(character) + 1], 
        functionName[functionName.index(character) + 1].upper())
    i = functionName.find('_')

Or even

# split on all the underscores
parts = functionName.split("_")
# then join - unchanged first word and capitalised rest
out = ''.join(
             [parts[0]] + [w.capitalize() for w in parts[1:]]
             )

This is unchecked, mind, as I'm on an iPad right now.

Guy
  • 637
  • 7
  • 15