Difference between these two for loops? - python

I would like to know how this loop:
def decrypt(s):
l = [chr(ord(c)-2) for c in s]
return ''.join(l)
print(decrypt("Ecguct"))
and this loop:
def decrypt(s):
for c in s:
l = chr(ord(c) - 2)
return ''.join(l)
print(decrypt("Ecguct"))
are different from one another.
I haven't seen this format of looping — [chr(ord(c)-2) for c in s] — before, and if someone could tell me how to read it, I would be grateful.

In the second piece of code, you are returning l right after the first loop. At that point, l's value is ['C'] and not ['Caesar'] as you were expecting.
def decrypt(s):
for c in s:
l = chr(ord(c) - 2)
return ''.join(l) # -> this is the culprit
print(decrypt("Ecguct"))
l = [chr(ord(c)-2) for c in s] -> this is called a list comprehension.
It's a concise way of generating lists and is equivalent to
l = []
for c in s:
l.append(chr(ord(c)-2))

Related

I want to make a list of list in python in loop

I want to get an output like
['a','aa','aaa','aaaa','aaaaa'],['b','bb','bbb','bbbb','bbbbb'],['c','cc','ccc','cccc','ccccc']
using the code snippet below
a = ["a","b","g","f"]
b =[1,2,3,4,5]
e = []
f = []
for i in a:
for j in b:
e.append(i*j)
f.append(e)
print(f)
Can anybody help me with this please
You're failing to reset e to a new empty list after each outer loop, so you're inserting a bunch of aliases to the same list. Just move the e = [] between the two fors (and indent to match), e.g.:
for i in a:
e = []
for j in b:
...
so you make a new list to populate on each loop.
Alternatively, as noted in the comments, a nested listcomp would do the trick:
f = [[x * y for x in b] for y in a]
removing the need for a named e at all (and running somewhat faster to boot).
It looks like you forgot to reset you e list with every iteration. This will yield your desired outcome:
a = ["a","b","g","f"]
b =[1,2,3,4,5]
e = []
f = []
for i in a:
e= []
for j in b:
e.append(i*j)
f.append(e)
print(f)
The only error is that you are not resetting e to [] after each iteration in a. Let me explain:
This is the code you need:
for i in a:
for j in b:
e.append(i*j)
f.append(e)
e = []
You need the e = [] at the end. If there was no e = [], at the end of thefirst iteration, e = ['a', 'aa'...]. During the second iteration, e would equal [a, aa, aaa, aaaa, aaaaa, b, bb, bbb...] However, setting it to an empty list stops this.
Python has a property that if u multiply a number with a string it will multiply that string for example:
print(2*"ab")
abab
You can use this for your answer:
a = ["a","b","g","f"]
b =[1,2,3,4,5]
c = [[x*y for x in b] for y in a]
print(c)
Lengthy, but this works fine too
a = ['a','b','c','d']
b = [1,2,3,4,5]
nest_array = []
for i in range(0,len(a)):
arr = []
for val in b:
str1 = ""
while len(str1) < val:
if len(str1) == val:
break
else:
str1 += a[i]
arr.append(str1)
nest_array.append(arr)
print(nest_array)

Remove adjacent duplicates given a condition

I'm trying to write a function that will take a string, and given an integer, will remove all the adjacent duplicates larger than the integer and output the remaining string. I have this function right now that removes all the duplicates in a string, and I'm not sure how to put the integer constraint into it:
def remove_duplicates(string):
s = set()
list = []
for i in string:
if i not in s:
s.add(i)
list.append(i)
return ''.join(list)
string = "abbbccaaadddd"
print(remove_duplicates(string))
This outputs
abc
What I would want is a function like
def remove_duplicates(string, int):
.....
Where if for the same string I input int=2, I want to remove my n characters without removing all the characters. Output should be
abbccaadd
I'm also concerned about run time and complexity for very large strings, so if my initial approach is bad, please suggest a different approach. Any help is appreciated!
Not sure I understand your question correctly. I think that, given m repetitions of a character, you want to remove up to k*n duplicates such that k*n < m.
You could try this, using groupby:
>>> from itertools import groupby
>>> string = "abbbccaaadddd"
>>> n = 2
>>> ''.join(c for k, g in groupby(string) for c in k * (len(list(g)) % n or n))
'abccadd'
Here, k * (len(list(g)) % n or n) means len(g) % n repetitions, or n if that number is 0.
Oh, you changed it... now my original answer with my "interpretation" of your output actually works. You can use groupby together with islice to get at most n characters from each group of duplicates.
>>> from itertools import groupby, islice
>>> string = "abbbccaaadddd"
>>> n = 2
>>> ''.join(c for _, g in groupby(string) for c in islice(g, n))
'abbccaadd'
Create group of letters, but compute the length of the groups, maxed out by your parameter.
Then rebuild the groups and join:
import itertools
def remove_duplicates(string,maxnb):
groups = ((k,min(len(list(v)),maxnb)) for k,v in itertools.groupby(string))
return "".join(itertools.chain.from_iterable(v*k for k,v in groups))
string = "abbbccaaadddd"
print(remove_duplicates(string,2))
this prints:
abbccaadd
can be a one-liner as well (cover your eyes!)
return "".join(itertools.chain.from_iterable(v*k for k,v in ((k,min(len(list(v)),maxnb)) for k,v in itertools.groupby(string))))
not sure about the min(len(list(v)),maxnb) repeat value which can be adapted to suit your needs with a modulo (like len(list(v)) % maxnb), etc...
You should avoid using int as a variable name as it is a python keyword.
Here is a vanilla function that does the job:
def deduplicate(string: str, treshold: int) -> str:
res = ""
last = ""
count = 0
for c in string:
if c != last:
count = 0
res += c
last = c
else:
if count < treshold:
res += c
count += 1
return res

Phone number sequence predictor

I have an Phone number here lets say : 98888888xx the last two XX are the numbers which I want to generate a sequence of like 988888811 , 9888888812, 988888813 and so on.
I am trying to learn python programming so can someone help me how would I go on writing a script for the same
You could use a list comprehension and range():
['98888888' + str(number).zfill(2) for number in range(100)]
['9888888800',
'9888888801',
'9888888802',
...
'9888888897',
'9888888898',
'9888888899']
A robust solution is to use recursion to handle many possible occurrences of "x":
import re
s = '98888888xx'
_len = len(re.sub('\d+', '', s))
def combos(d, current = []):
if len(current) == _len:
yield current
else:
for i in d[0]:
yield from combos(d[1:], current+[i])
_c = combos([range(1, 10)]*_len)
new_result = [(lambda d:re.sub('x', lambda _:str(next(d)), s))(iter(i)) for i in _c]
Output:
['9888888811', '9888888812', '9888888813', '9888888814', '9888888815', '9888888816', '9888888817', '9888888818', '9888888819', '9888888821', '9888888822', '9888888823', '9888888824', '9888888825', '9888888826', '9888888827', '9888888828', '9888888829', '9888888831', '9888888832', '9888888833', '9888888834', '9888888835', '9888888836', '9888888837', '9888888838', '9888888839', '9888888841', '9888888842', '9888888843', '9888888844', '9888888845', '9888888846', '9888888847', '9888888848', '9888888849', '9888888851', '9888888852', '9888888853', '9888888854', '9888888855', '9888888856', '9888888857', '9888888858', '9888888859', '9888888861', '9888888862', '9888888863', '9888888864', '9888888865', '9888888866', '9888888867', '9888888868', '9888888869', '9888888871', '9888888872', '9888888873', '9888888874', '9888888875', '9888888876', '9888888877', '9888888878', '9888888879', '9888888881', '9888888882', '9888888883', '9888888884', '9888888885', '9888888886', '9888888887', '9888888888', '9888888889', '9888888891', '9888888892', '9888888893', '9888888894', '9888888895', '9888888896', '9888888897', '9888888898', '9888888899']
Note that a simple solution in the form of a nested list comprehension presents itself when there are only two 'x':
d = [s.replace('x', '{}').format(a, b) for a in range(1, 10) for b in range(1, 10)]
However, multiple nested loops is not a clean approach to solving the problem when the input string contains three or more 'x's. Instead, recursion works best:
s = '98888888xxxxx'
_len = len(re.sub('\d+', '', s))
_c = combos([range(1, 10)]*_len)
new_result = [(lambda d:re.sub('x', lambda _:str(next(d)), s))(iter(i)) for i in _c]
Output (first twenty strings):
['9888888811111', '9888888811112', '9888888811113', '9888888811114', '9888888811115', '9888888811116', '9888888811117', '9888888811118', '9888888811119', '9888888811121', '9888888811122', '9888888811123', '9888888811124', '9888888811125', '9888888811126', '9888888811127', '9888888811128', '9888888811129', '9888888811131', '9888888811132']

Repeating characters results in wrong repetition counts

My function looks like this:
def accum(s):
a = []
for i in s:
b = s.index(i)
a.append(i * (b+1))
x = "-".join(a)
return x.title()
with the expected input of:
'abcd'
the output should be and is:
'A-Bb-Ccc-Dddd'
but if the input has a recurring character:
'abccba'
it returns:
'A-Bb-Ccc-Ccc-Bb-A'
instead of:
'A-Bb-Ccc-Cccc-Bbbbb-Aaaaaa'
how can I fix this?
Don't use str.index(), it'll return the first match. Since c and b and a appear early in the string you get 2, 1 and 0 back regardless of the position of the current letter.
Use the enumerate() function to give you position counter instead:
for i, letter in enumerate(s, 1):
a.append(i * letter)
The second argument is the starting value; setting this to 1 means you can avoid having to + 1 later on. See What does enumerate mean? if you need more details on what enumerate() does.
You can use a list comprehension here rather than use list.append() calls:
def accum(s):
a = [i * letter for i, letter in enumerate(s, 1)]
x = "-".join(a)
return x.title()
which could, at a pinch, be turned into a one-liner:
def accum(s):
a = '-'.join([i * c for i, c in enumerate(s, 1)]).title()
This is because s.index(a) returns the first index of the character. You can use enumerate to pair elements to their indices:
Here is a Pythonic solution:
def accum(s):
return "-".join(c*(i+1) for i, c in enumerate(s)).title()
simple:
def accum(s):
a = []
for i in range(len(s)):
a.append(s[i]*(i+1))
x = "-".join(a)
return x.title()

How to write all_subsets function using recursion?

Given the function all_subsets(lst), how can I write this function using recursion?
For example of input: [1,2,3], the output should be: [[], [1],[2],[3],[1,2],[1,3],[2,3][1,2,3]]
The assignment is to use recursive function. Please help. This is part of a lab assignment, so I am not graded on this, but at the same time, I am dying to learn how to write this code, and I don't know anyone in my lab class that's figured it out.
So far, I've got:
def all_subsets(b):
if len(b) == 0:
return ''
else:
lst = []
subsets = all_subsets(b[1:])
for i in b:
lst.append([i])
for i in subsets:
if b[0] not in i:
lst.append([b[0]] + i)
for i in subsets:
if b[1] not in i:
lst.append([b[1]] + i)
return lst
It can handle [1,2,3], but it can't handle anything bigger; plus this code also has weird output order
This should work:
def all_subsets(b):
if len(b)==1:
return [[], b] # if set has 1 element then it has only 2 substets
else:
s = all_subsets(b[:-1]) # calculate subsets of set without last element
# and compose remaining subsets
return sorted(s + [e + [b[-1]] for e in s], key=len) # you can omit sorting if you want

Categories

Resources