There is something wrong with my Python code involve the replace function - python

I was creating a function about replacing multiple part of a string with multiple number but for some reason, the output is not what i expected.
from pyprimes import isprime
def prime_replace(x, l = []):
lst = []
string = str(x)
for n in range(10):
for i in l:
string = string.replace(string[i], str(n))
lst.append(int(string))
return lst
print prime_replace(x = 56243, l = [2, 3])
The output of this function is a list [56003, 56113, 56223, 56333, 56444, 56555, 66666, 77777, 88888, 99999] but what i wanted is [56003, 56113, 56223, 56333, 56443, 56553, 56663, 56773, 56883, 56993] Can someone help me with this and tell me what went wrong, thank you.

You're modifying the original string, and replace is replacing "all occurrences"
Here's part of a print out for how it's generating the output you see:
...
string is now 56333
replacing 3 with 3
string is now 56333
replacing 3 with 4
string is now 56444
...
You can see that we successfully got 56333 like you wanted, but then you wanted to replace str[2] (actual value: 3) with str(n) (actual value: 4), which replaced all occurrences of 3
One workaround for this specific scenario is to make a copy of your string:
def prime_replace(x, l = []):
lst = []
string = str(x)
for n in range(10):
newstr = string
for i in l:
newstr = newstr.replace(string[i], str(n))
lst.append(int(newstr))
return lst
print prime_replace(x = 56243, l = [2, 3])
Output:
[56003, 56113, 56223, 56333, 56443, 56553, 56663, 56773, 56883, 56993]
Demo (not recommended)
However, replace may not be the best choice since it will replace all occurrences of the "oldstring" with the "newstring". It looks like what you're really trying to do is to change string at indices in l to be the next value in range(10), we can do this through indexing and appending:
def prime_replace(x, l = []):
lst = []
string = str(x)
for n in range(10):
newstr = string
for i in l:
newstr = newstr[0:i]+str(n)+newstr[i+1:];
lst.append(int(newstr))
return lst
Demo (better)

string.replace(a,b) replaces all instances of a with b. so, '56243'.replace(3,0) evaluates to '56240'. You are trying to replace the values at the indicies in l, so you should replace
string = string.replace(string[i], str(n))
with
string = string[:i] + str(n) + string[i+1:]
The other answer fails on cases where the value at an index to be replaced is repeated elsewhere in the number, i.e. prime_replace(562432,[2,3])

I have also found another solution for it by turning the string into list
from pyprimes import isprime
def prime_replace(x, l):
lst = []
string_lst = list(str(x))
for n in range(10):
for i in l:
string_lst[i] = str(n)
lst.append(int("".join(string_lst)))
return lst
print prime_replace(56243, [2, 3])
Output
[56003, 56113, 56223, 56333, 56443, 56553, 56663, 56773, 56883, 56993]
But thank you very much for your solution.

You really want a mutable data-structure here to simplify your logic. You can get what is essentially a mutable string by using a bytearray. So here is an alternative approach:
In [11]: def prime_replace(x, l=None):
...: if l is None:
...: l = [] # careful with mutable default arguments
...: lst = []
...: string = bytearray(str(x), encoding='ascii')
...: for n in b'0123456789':
...: for i in l:
...: string[i] = n
...: lst.append(int(string))
...: return lst
...:
In [12]: prime_replace(x = 56243, l = [2, 3])
Out[12]: [56003, 56113, 56223, 56333, 56443, 56553, 56663, 56773, 56883, 56993]
Also, be careful with mutable default arguments.
I use a bytearray because I think it simplifies things more. A list also works. But note with bytearray and by iterating directly over the ascii b'012345679' it simplifies everything by a lot, and you don't have to keep converting things back-and-forth between str and int

Related

Accumulating lists with letters

I'm trying to right a code that accumulates a list.
The code I've figured out so far does that but I want to make it work with letters e.g.
accumulate("a", "b", "c")
would come out to be a, ab, abc.
def accumulate(L):
theSum = 0
for i in L:
theSum = theSum + i
print(theSum)
return theSum
accumulate([1, 2, 3])
While #WillemVanOnsem has provided you with the method that would work, to shorten your code you can use itertools.accumulate from the standard library:
>>> from itertools import accumulate
>>>
>>> for step in accumulate(['a', 'b', 'c']):
print(step)
a
ab
abc
>>>
You can try this:
import string
l = string.ascii_lowercase
the_list = []
letter = ""
for i in l:
letter += i
the_list.append(letter)
Even better in a function with a generator:
def accumulation():
l = string.ascii_lowercase
letter = ""
for i in l:
letter += i
yield letter
the_letters = list(accumulation())
print(the_letters)
Output:
['a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefg', 'abcdefgh', 'abcdefghi', 'abcdefghij', 'abcdefghijk', ...]
If you want to let it work with strings, you have to initialize it with an empty string:
def accumulate(*args):
theSum = ''
for i in args:
theSum += i # we can here shorten it to += (kudos to #ChristianDean)
print(theSum)
return theSum
Furthermore if you want to use an arbitrary number of arguments, you should use *args (or *L).
Now of course this will no longer work with numbers. The theSum += i is here short for theSum = theSum + i (since strings are immutable). Note however that this is not always the case: for lists there is a difference for instance.
Now it prints:
>>> accumulate("a", "b", "c")
a
ab
abc
'abc'
The last 'abc' is not a result of a print(..) statement, but it is the return of the accumulate function.

How can I write a list without duplicate with only for, if and boolean

My professor gave me an exercise where I write a function that returns a list without the duplicate to the old list.
This is the code but I don't know how to write the method without using .remove():
def distinct(lst):
lstnew = []
c = range(len(lst))
for i in range(len(lst)):
if i in range(len(lst)) != c:
lstnew += [i]
c += 1
return lstnew
print distinct([1,3,1,2,6])
print distinct([['a','ab','a','ab']])
I forgot to write an important thing, I must preserve order in the output list.
[UPDATE]
After I read the answer of Jai Srivastav I code this:
def distinct(lst):
lstnew = []
for element in lst:
if element not in lstnew:
lstnew = lstnew + [element]
return lstnew
And It works perfectly
def distinct(lst):
dlst = []
for val in lst:
if val not in dlst:
dlst.append(val)
return dlst
Is this considered cheating?
>>> distinct = lambda lst: list(set(lst))
>>> distinct([1,3,1,2,6])
[1, 2, 3, 6]
>>> distinct(['a','ab','a','ab'])
['a', 'ab']
If order isn't important, you can cast it to a set, then back to a list
def distinct(lst):
return list(set(lst))
If you need to eliminate duplicates AND preserve order you can do this:
def distinct(lst):
seen = set()
for item in lst:
if item not in seen:
yield item
seen.add(item)
a = [1,3,1,2,6]
print(list(distinct(a)))
[1,3,2,6]
b = ['a','ab','a','ab']
print(list(distinct(b)))
['a', 'ab']
See a demo here: https://ideone.com/a2khCg
There are Excellent Solutions That I Already Applied. But my professor said us that we don't must use the methods of the list. Has anyone else got any more thoughts?

Modify a int list in function in python

I want to rotate k element in a list in python. For example, n = 7, k = 3, and the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Here is the statement I wrote. It seems to work in the command line.
nums = nums[k%len(nums):] + nums[:k%len(nums)]
But when I encapsulate it in a function like:
def rotate(nums, k):
nums = nums[k%len(nums):] + nums[:k%len(nums)]
return
I want to modify nums directly, but this function doesn't work.
I know that I can use a for loop like:
for i in range(k):
nums.insert(0,nums.pop())
but I want to know why the previous method doesn't work?
What you want is a slice assignment:
nums[:] = nums[k%len(nums):] + nums[:k%len(nums)]
This mutates the list that was passed in, so the change is visible after the function returns. Assigning just to nums merely makes nums point to a different list inside the function; it doesn't affect the original list.
Are you sure you want to modify nums? You need not create a separate list even if you do not modify nums. One advantage of the following approach is that it will work with any sequence.
from itertools import islice
def rotate(lst, k):
n = len(lst)
start = n - (k % n) #handle all ints
for item in islice(lst, start, None):
yield item
for item in islice(lst, 0, start):
yield item
If you insist on modifying nums as you say, you can still do so. E.g.,
nums = [x + 1 for x in range(7)]
nums[:] = rotate(nums,-10)
The nums that is inside the function is only within that function. So you need to return it from that function. One way is like this (python 2.7 code, add parentheses to print if you use 3.x):
nums = [1, 2, 3, 4, 5, 6, 7]
k = 3
def rotate(nums, k):
return nums[k%len(nums):] + nums[:k%len(nums)]
print 'Original', nums
nums = rotate(nums, k)
print 'Rotated', nums

How can I get rid of having lists inside of a list in python?

def digit_sum(n):
n = str(n)
empty = [x.split() for x in n]
print empty
digit_sum(21)
This code will output:
[['2'], ['1']]
What I need is to make it:
[2, 1]
so I can add the numbers in the list together. How do I do that?
I would just do (you don't need to .split it, just convert it to a string over which you can iterate):
def digit_sum(n):
empty = [int(d) for d in str(n)]
print empty
Demo:
>>> digit_sum(21)
[2, 1]
You could then obviously add them together with the sum() function.
Don't call split. Split tries to divide a string on whitespace (by default). It will return a list of strings always (even if there were no splits to be made).
Since your loop is over a string, x will be a single character. That means you'll never have any whitespace to split on.
Just do [x for x in n] or list(n).
Or if you want your digits as integers, rather than strings: [int(x) for x in n] or map(int, n)
I believe if you
firstlist.append(secondlist)
Python should print 1, 2. Posting from a Windows Phone, so I can't run the code, excuse my incompetence if this doesn't work.
This works:
def digit_sum(n):
n = str(n)
empty = [int(x) for x in n]
print empty
digit_sum(21)
Output:
[2,1]
split returns a list. for x in n will go over each character in the string and int(x) converts the character to an integer.
>>> from itertools import chain
>>> x = [['2'], ['1']]
>>> map(int, chain(*x))
[2, 1]
If you want to completely flatten a list of lists, you need to check if its iterable.
To do this you can create a generator which returns a non-iterable item, or recursively calls itself if the item is iterable.
Then place each element of the generator in a list, and print it.
Warning!!
This will crash with cycling lists ie l = []; l.append(l)
def get_single_elements(item):
if hasattr(item, '__iter__'):
for child_item in item:
for element in get_single_elements(child_item):
yield element
else:
yield item
def print_flat(item):
print [element for element in get_single_elements(item)]
>>> print_flat([[[0],[1]]])
[0, 1]
>>> print_flat([[[[[0,[1,2,[3,4]]]]],[[1]]]])
[0, 1, 2, 3, 4, 1]
Edit if you're sure you want to convert all items to ints, then write it like this
def print_flat(item):
print [int(element) for element in get_single_elements(item)]

How to make a program that doubles letters (input = hello, returns = hheelllloo)

This is what i have so far - but it prints [tt],[tt,hh],[tt,hh,ee] when i input 'the'
def doubleChar(doubleit):
doubled = []
for letter in doubleit:
doubled.append(letter * 2)
print(doubled)
doubleChar('the')
Not a great attempt but couldn't really think of another way.
Put your print outside the loop, and also transform it back to string instead of a list of letters.
def doubleChar(doubleit):
doubled = []
for letter in doubleit:
doubled.append(letter * 2)
print("".join(doubled))
doubleChar('the')
Btw a function is not even needed, a simple one liner:
>>> r = "the"
>>> "".join(x*2 for x in r)
'tthhee'
I would do it with a zip:
>>> def duplicator(s, n=2):
... return ''.join(x for t in zip(*[s] * n) for x in t)
...
>>> duplicator('the')
'tthhee'
>>> duplicator('potato', 3)
'pppoootttaaatttooo'
You are almost there. You just need to attach the different entries in the doubled list.
def doubleChar(doubleit):
doubled = []
for letter in doubleit:
doubled.append(letter * 2)
# at this stage you have ['tt', 'hh', 'ee']
# you can join them into a str object
return "".join(doubled)
You can also use a lambda combined with a comprehention to do it:
doubleChar = lambda s : "".join([e*2 for e in s])
Or you can keep your loop but use an str object without going through the list:
s = "the"
d = ""
for c in s:
d = d + e*2
print(d)
>> 'tthhee'
Just for fun, here is an unusual way using extended slicing
>>> s='the'
>>> (s+(' '+s*2)*len(s))[::len(s)+1]
'tthhee'
>>> s="hello world"
>>> (s+(' '+s*2)*len(s))[::len(s)+1]
'hheelllloo wwoorrlldd'
reduce(lambda s, c: s + c + c, "hello", "")

Categories