6

I'm writing a function to replace every n-th letter from a string

def replaceN(str, n):
   for i in range(len(str)):
     n=str[i]
     newStr=str.replace(n, "*")
     return newStr

but when i execute it, only the first letter is replaced with a *. I'm sure there is a mistake, but i can't see it.

Eric
  • 95,302
  • 53
  • 242
  • 374
trek
  • 69
  • 1
  • 1
  • 2

7 Answers7

11

One-liner:

newstring = ''.join("*" if i % n == 0 else char for i, char in enumerate(string, 1))

Expanded:

def replace_n(string, n, first=0):
    letters = (
        # i % n == 0 means this letter should be replaced
        "*" if i % n == 0 else char

        # iterate index/value pairs
        for i, char in enumerate(string, -first)
    )
    return ''.join(letters)
>>> replace_n("hello world", 4)
'*ell* wo*ld'
>>> replace_n("hello world", 4, first=-1)
'hel*o w*orl*'
Eric
  • 95,302
  • 53
  • 242
  • 374
2

Your code has several problems:

First, the return in the wrong place. It is inside the for loop but it should be outside. Next, in the following fragment:

for i in range(len(str)):
    n=str[i]
    newStr=str.replace(n, "*")

the n that you passed as the second argument to your function is being overwritten at every loop step. So if your initial string is "abcabcabcd" and you pass n=3 (a number) as a second argument what your loop does is:

n="a"
n="b"
n="c"
...

so the value 3 is never used. In addition, in your loop only the last replacement done in your string is saved:

n="a"
newStr="abcabcabcd".replace("a", "*") --> newStr = "*bc*bc*bcd"
n="b"
newStr="abcabcabcd".replace("b", "*") --> newStr = "a*ca*ca*cd"
...
n="d"
newStr="abcabcabcd".replace("d", "*") --> newStr = "abcabcabc*"

If you test your function (after fixing the return position) with some strings it seems to work fine:

In [7]: replaceN("abcabcabc", 3)
Out[7]: 'ab*ab*ab*'

but if you do the choice more carefully:

In [10]: replaceN("abcabcabcd", 3)
Out[10]: 'abcabcabc*'

then it is obvious that the code fails and it is equivalent to replace only the last character of your string:

my_string.replace(my_string[-1], "*")

The code given by Eric is working fine:

In [16]: ''.join("*" if i % 3 == 0 else char for i, char in enumerate("abcabcabcd"))
Out[16]: '*bc*bc*bc*'

It replaces positions 3rd, 6th, 9th and so on. It may need some adjustment if you don't want the position 0 being replaced too.

Vicent
  • 5,322
  • 2
  • 28
  • 36
1

Try this:

def replaceN(string, n):
    s = ''
    for i in range(len(string)):
        if i%n!=0:
            s += string[i]
        else:
            s += '*'
    return s
Pravitha V
  • 3,308
  • 4
  • 33
  • 51
Kosta
  • 19
  • 1
0

You've put your return within the loop so after the first iteration it returns the string without the rest of the string being replaced. The string should be returned once the loop has completely finished. Something like this should work:

def replaceN(str, n):
    for i in range(len(str)):
        n=str[i]
        newStr=str.replace(n, "*")
    return newStr
Suhail Patel
  • 13,644
  • 2
  • 44
  • 49
  • This is wrong and doesn't work. The n argument is not used for indicating a position in the string and changes made on the original string at a given loop step are lost in the next step. This function has the same effect that mystring.replace(mystring[-1], "*"). – Vicent Dec 02 '12 at 14:01
0

Another option, using re:

import re
def repl(matchobj):
    return re.sub('.$','*',matchobj.group(0))
def replaceN(str, n):
    return re.sub('.'*n,repl,str)

However, this needs additional library. If you have this library already imported, this may be a shorthand method.

anishsane
  • 20,270
  • 5
  • 40
  • 73
0

I like Eric's answer, but you indicated in the comment that the first letter shouldn't be replaced. You can adapt the code like this then:

''.join("*" if i % n == 0 else char for i, char in enumerate(string, 1))
                                             the difference is here  ^

def replaceN(s, n):
    return ''.join(
      '*' if not i%n else char for i, char in enumerate(s, 1))

>>> replaceN('welcome', 3)
'we*co*e'
Lev Levitsky
  • 63,701
  • 20
  • 147
  • 175
0

A simple way by redefining string every time:

def replaceN(string, n):
    for i in range(n, len(string), n):
        string = string[:i-1] + "*" + string[i:]
    return string


In [1]: replaceN("abcabcabc", 3)
Out[1]: ab*ab*ab*
In [2]: replaceN("abcd abcd abcd", 4)
Out[2]: abcd*abcd*abcd