How to make enumerate not count blank's indexes?

Viewed 94

I am trying to writing a function which will take a sentence and make each odd letter an uppercase one and each even letter a lowercase one.

Here is what I tried:

def func(st):
    res = []
    for index, c in enumerate(st):
        if index % 2 == 0:
            res.append(c.upper())
        else:
            res.append(c.lower())
    return ''.join(res)
print(myfunc(something))

When the input is "Hello my guy", the output is "HeLlO My gUy" and not "HeLlO mY gUy" because it counts blank as a letter, what can I do?

7 Answers

I'd write it like this:

from itertools import cycle

def my_func(st):
    operation = cycle((str.upper, str.lower))
    conv = [next(operation)(c) if c != ' ' else c for c in st]
    return ''.join(conv)

Demo:

>>> my_func("Hello my guy")
'HeLlO mY gUy'

While I strongly recommend @timgeb's approach, here is another look at this:

import itertools

def my_func(st):
    
    st_double = st.replace(" ", "  ")
    return "".join(itertools.chain.from_iterable(
                    itertools.zip_longest(
                        st_double[::2].upper(), 
                        st_double[1::2].lower(),
                        fillvalue=""))
                  ).replace("  ", " ")

my_func("Hello my guy")
'HeLlO mY gUy'

Use indexing to split the string into even and odd, apply str.upper to even, and zip back together. Spaces are being taken care of by doubling them first.

def my_func(st):
    res = []
    index = 0
    for c in st:
        res.append(c.upper() if index%2==0 else c.lower())
        if c != " ":
            index += 1
    return ''.join(res)
Sentence = "Hello my guy"
print(my_func(Sentence))  # HeLlO mY gUy

enumerate() method adds counter to an iterable and returns it. It seems easier to count the index you need directly than to use the index returned by the enumerate.

You can use a separate index variable to count letters as follows:

def my_func(st):
    res = []
    i = 1
    for j in st:
        if j != " ":
            if i%2 == 0:
                res.append(j.lower())
            else:
                res.append(j.upper())
            i += 1
        else:
            res.append(" ")
    return ''.join(res)
print(my_func("Hello my guy"))

Output:

HeLlO mY gUy

See another solution using while loop:

Sentence = "hello my guy stack test"
def my_func(st):
    res = []
    counter = 0
    j = 0
    while counter < len(st):
        if st[counter] == ' ':
            res.append(st[counter])
        else:
            if j%2 == 0:
                res.append(st[counter].upper())
            else:
                res.append(st[counter].lower())
            j +=1
        counter +=1
    return ''.join(res)

print(my_func(Sentence))

See the output as:

enter image description here

Based on mcsoini's space-doubler:

def my_func(st):
    s = st.replace(" ", "  ")
    a = list(s.lower())
    a[::2] = s[::2].upper()
    return "".join(a).replace("  ", " ")

Benchmark with your original string and a longer one, speeds in million characters per second:

Length 12:
 8.3 mc/s ± 0.0 mc/s  Kelly
 5.7 mc/s ± 0.0 mc/s  mcsoini
 5.0 mc/s ± 0.0 mc/s  timgeb
 4.0 mc/s ± 0.0 mc/s  original
 4.0 mc/s ± 0.0 mc/s  Cardstdani
 3.9 mc/s ± 0.0 mc/s  Lazyer
 2.8 mc/s ± 0.0 mc/s  Baris

Length 129999:
23.4 mc/s ± 0.3 mc/s  Kelly
15.7 mc/s ± 0.2 mc/s  mcsoini
 6.0 mc/s ± 0.1 mc/s  timgeb
 3.7 mc/s ± 0.0 mc/s  Cardstdani
 3.5 mc/s ± 0.0 mc/s  original
 3.4 mc/s ± 0.0 mc/s  Lazyer
 2.4 mc/s ± 0.0 mc/s  Baris

(Note: while the original doesn't do it right and I didn't check all others, I was curious about the speeds in any case.)

Code (Try it online!):

from itertools import cycle

def original(st):
    res = []
    for index, c in enumerate(st):
        if index % 2 == 0:
            res.append(c.upper())
        else:
            res.append(c.lower())
    return ''.join(res)

def timgeb(st):
    operation = cycle((str.upper, str.lower))
    conv = [next(operation)(c) if c != ' ' else c for c in st]
    return ''.join(conv)

def Cardstdani(st):
    res = []
    i = 1
    for j in st:
        if j != " ":
            if i%2 == 0:
                res.append(j.lower())
            else:
                res.append(j.upper())
            i += 1
        else:
            res.append(" ")
    return ''.join(res)

def Baris(st):
    res = []
    counter = 0
    j = 0
    while counter < len(st):
        if st[counter] == ' ':
            res.append(st[counter])
        else:
            if j%2 == 0:
                res.append(st[counter].upper())
            else:
                res.append(st[counter].lower())
            j +=1
        counter +=1
    return ''.join(res)

import itertools

def mcsoini(st):
    st_double = st.replace(" ", "  ")
    return "".join(itertools.chain.from_iterable(
                    itertools.zip_longest(
                        st_double[::2].upper(), 
                        st_double[1::2].lower(),
                        fillvalue=""))
                  ).replace("  ", " ")

def Lazyer(st):
    res = []
    index = 0
    for c in st:
        res.append(c.upper() if index%2==0 else c.lower())
        if c != " ":
            index += 1
    return ''.join(res)

def Kelly(st):
    s = st.replace(" ", "  ")
    a = list(s.lower())
    a[::2] = s[::2].upper()
    return "".join(a).replace("  ", " ")

funcs = [original, timgeb, Cardstdani, Baris, mcsoini, Lazyer, Kelly]

from timeit import timeit
from random import shuffle
from statistics import mean, stdev

def test(s, number):
  print(f'Length {len(s)}:')
  speeds = {f: [] for f in funcs}
  def stats(func):
    ss = [s/1e6 for s in sorted(speeds[func])[-5:]]
    return '%4.1f mc/s ± %.1f mc/s ' % (mean(ss), stdev(ss))
  for _ in range(25):
    for func in funcs:
      t = timeit(lambda: func(s), 'gc.enable()', number=number) / number
      speeds[func].append(len(s) / t)

  for f in sorted(funcs, key=stats, reverse=True):
    print(stats(f), f.__name__)
  print()

s = "Hello my guy"
test(s, 10000)
test(" ".join([s] * 10000), 1)

Iterate over string's characters and classify with a str.isspace criteria.

def camel_characters(s):
    new_s = ''
    counter = 0
    for char in s:
        if char.isspace():
            new_s += char
        else:
            if counter % 2:
                new_s += char.lower()
            else:
                new_s += char.upper()
                counter = 0 # can be commented as well
            counter += 1

    return new_s

s = "Hello my guyy"
print(camel_characters(s))
Related