Mamba
Mamba

Reputation: 1203

How to loop properly and replace element in string?

I'm trying to understand the replace method. I have a string of numbers where I would like to make some adjustments. Particularly, I would categorize the numbers based on a threshold:

def makeAdjustment(x):
    for each in x:
        if int(each) < 5:
            x = x.replace(each, "0")
        else:
            x = x.replace(each, "1")
    return x

In use:

>>> makeAdjustment("800857237867") == "100111001111" 
True
>>> makeAdjustment("15889923") == "01111100" 
True
>>> makeAdjustment("14963896") == "00110111" 
True

However, if the sequence of numbers gets larger the string is converted to zero:

>>> makeAdjustment("366058562030849490134388085")      
'000000000000000000000000000'

Upvotes: 2

Views: 108

Answers (4)

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160377

The issue here isn't that the string is larger, it's that instances of '1' or '0' shouldn't be replaced. When you encounter a '1' later on in the string (as in your last case) you'll replace all previous instances of '1' with '0'.

One solution is to special case them:

def makeAdjustment(x):
    for each in x:
        if int(each) in {'1', '0'}:
            continue
        if int(each) < 5:
            x = x.replace(each, "0", 1)
        else:
            x = x.replace(each, "1")
    return x

This is one option but it isn't the best since you're calling replace for every iteration. You can do much better by just using join here:

def makeAdjustment(x):
    return "".join("1" if int(i) > 5 else "0" for i in x)

which is cleaner, clearer and faster:

# for loop 
%timeit makeAdjustment("366058562030849490343880185")
10000 loops, best of 3: 39.1 µs per loop
# join
%timeit makeAdjustment("366058562030849490343880185")
100000 loops, best of 3: 17.7 µs per loop

Upvotes: 3

shahkalpesh
shahkalpesh

Reputation: 33476

def makeAdjustment(x):
    splitted = [c for c in enumerate(x)]
    for each in splitted:
        if int(each[1]) < 5:
            splitted[each[0]] = "0"
        else:
            splitted[each[0]] = "1"

    return "".join(splitted)

EDIT: The above code splits the string and makes a list of tuple where each character in the string is accompanied by index of it. This is done using enumerate.

The list (splitted) is enumerated where each[1] will contain the actual character from the string whereas each[0] will contain the index of the actual character in the given string.

Based on the value contained in each[1] the value contained at each[0] index of the splitted is modified.

In the end, the string is joined using "".join(splitted).

Upvotes: 1

jwillis0720
jwillis0720

Reputation: 4477

My way of doing it is just to turn it into a list and go by position. It's so much easier since replace is replacing All each characters no matter where the position Here we use each as an index to keep track of our location in the list

def makeAdjustment(x):
    x = list(x)
    for each in range(len(x)+1):
        if int(x[each]) < 5:
            x[each] = '0'
        else:
            x[each] = '1'
        #Turn it back into a string instead of a list 
        x = "".join(x)
        return x

Upvotes: 1

Daniel
Daniel

Reputation: 42748

If you want to replace each character, build a new string character by character instead of using replace:

def makeAdjustment(x):
    result = []
    for each in x:
        if int(each) < 5:
            result.append("0")
        else:
            result.append("1")
    return ''.join(result)

or in short:

def makeAdjustment(x):
    return ''.join("0" if int(each) < 5 else "1" for each in x)

Upvotes: 2

Related Questions