Related
I have created a function which randomly generates a list of the letters "a", "b", "c", and "d". I would like to create a new list which is the same as the first list but with any letters/items which are the same as the previous letter/item removed. Where I am having problems is referring to the previous letter in the list.
For example, if :
letterlist = ['a','a','a','b','b','a,',b']
then the output should be,
nondupelist = ['a','b','a','b']
The problem is that nodupeletterlist is the same as letterlist - meaning it's not removing items which are the same as the last - because I am getting the function to refer to the previous item in letterlist wrong. I have tried using index and enumerate, but I am obviously using them wrong because I'm not getting the correct results. Below is my current attempt.
import random
def rdmlist(letterlist, nodupeletterlist):
for item in range(20):
rng = random.random()
if rng < 0.25:
letterlist.append("a")
elif 0.25 <= rng and rng < 0.5:
letterlist.append("b")
elif 0.5 <= rng and rng < 0.75:
letterlist.append("c")
else:
letterlist.append("d")
for letter in letterlist:
if letter != letterlist[letterlist.index(letter)-1]:
nodupeletterlist.append(letter)
else:
pass
return
letterlist1 = []
nodupeletterlist1 = []
rdmlist(letterlist1, nodupeletterlist1)
EDIT:
This is what I ended up using. I used this solution simply because I understand how it works. The answers below may provide more succinct or pythonic solutions.
for index, letter in enumerate(letterlist, start=0):
if 0 == index:
nodupeletterlist.append(letter)
else:
pass
for index, letter in enumerate(letterlist[1:], start = 1):
if letter != letterlist[index-1]:
nodupeletterlist.append(letter)
else:
pass
for i, letter in enumerate(([None]+letterlist)[1:], 1):
if letter != letterlist[i-1]:
nodupeletterlist.append(letter)
You can use itertools.groupby:
import itertools
nodupeletterlist = [k for k, _ in itertools.groupby(letterlist)]
Solution without using itertools, as requested in the comments:
def nodupe(letters):
if not letters:
return []
r = [letters[0]]
for ch in letters[1:]:
if ch != r[-1]:
r.append(ch)
return r
nodupeletterlist = nodupe(letterlist)
A fixed version of the proposed "working solution":
def nodupe(letters):
if not letters:
return []
r = [letters[0]]
r += [l for i, l in enumerate(letters[1:]) if l != letters[i]]
return r
nodupeletterlist = nodupe(letterlist)
You can also simplify your random generator a bit, by using random.choices:
import random
chars = 'abcd'
letterlist = random.choices(chars, k=20)
or by using random.randint:
import random
start, end = ord('a'), ord('d')
letterlist = [chr(random.randint(start, end)) for _ in range(20)]
Here's what I came up with. Using random.choices() would be better than what I have below, but same idea. doesn't involve itertools
>>> li_1 = [random.choice("abcdefg") for i in range(20)]
>>> li_1
['c', 'e', 'e', 'g', 'b', 'd', 'b', 'g', 'd', 'c', 'e', 'g', 'e', 'c', 'd',
'e', 'e', 'f', 'd', 'd']
>>>
>>> li_2 = [li_1[i] for i in range(len(li_1))
... if not i or i and li_1[i - 1] != li_1[i]]
>>> li_2
['c', 'e', 'g', 'b', 'd', 'b', 'g', 'd', 'c', 'e', 'g', 'e', 'c',
'd', 'e', 'f', 'd']
The problem with the way that you are using letterlist.index(letter)-1 is that list.index(arg) returns the the index of the first occurrence of arg in list, in this case the letter. This means that if you have list = ["a", "b", "a"] and you run list.index("a") it will always return 0.
A way to do what you intend to (removing consecutive repetitions of letters) would be:
nodupeletterlist.append(letterlist[0])
for idx in range(1, len(letterlist)):
if letterlist[idx] != letterlist[idx-1]:
nodupeletterlist.append(letterlist[idx])
Do This:
L1 = ['a','a','a','b','b','c','d']
L2 = []
L2.append(L1[0])
for i in range(1,len(L1)):
if L1[i] != L1[i-1]:
L2.append(L1[i])
set() will create a set with only unique values,then the list() will convert it back to a a list containing values without any repetition.
I hope this helps...
This code generates the list of all permutations:
def permute(xs, low=0):
if low + 1 >= len(xs):
yield xs
else:
for p in permute(xs, low + 1):
yield p
for i in range(low + 1, len(xs)):
xs[low], xs[i] = xs[i], xs[low]
for p in permute(xs, low + 1):
yield p
xs[low], xs[i] = xs[i], xs[low]
for p in permute(['A', 'B', 'C', 'D']):
print p
What I would like to do create an index for the list of permutations so that if I call a number I can access that particular permutation.
For example:
if index.value == 0:
print index.value # ['A','B','C','D']
elif index.value == 1:
print index.value # ['A','B','D','C']
#...
I am new to Python, thank you in advance for any guidance provided.
You can also create a new function getperm to get the permutation index from your generator:
def getperm(index,generator):
aux=0
for j in generator:
if aux == index:
return j
else:
aux = aux +1
In: getperm(15,permute(['A', 'B', 'C', 'D']))
Out: ['C', 'A', 'D', 'B']
Iterators does not support "random access". You'll need to convert your result to list:
perms = list(permute([....]))
perms[index]
Like levi said, it sounds like you want to use a dictionary.
The dictionary will look something like this:
#permDict = {0:['A', 'B', 'C', 'D'], 1:['A', 'B', 'D', 'C'], ...}
permDict = {}
index = 0
for p in permute(['A', 'B', 'C', 'D']):
permDict[index] = p
index += 1
Then just get a value according to the key you have assigned.
if index == 0:
print permDict[0] # ['A','B','C','D']
elif index == 1:
print permDict[1] # ['A','B','D','C']
#...
Or just store each permutation in a list and call those indices.
permList = [p for p in permute(['A', 'B', 'C', 'D'])]
#permList[0] = ['A', 'B', 'C', 'D']
#permlist[1] = ['A', 'B','D', 'C']
You can generate the desired permutation directly (without going through all previous permutations):
from math import factorial
def permutation(xs, n):
"""
Return the n'th permutation of xs (counting from 0)
"""
xs = list(xs)
len_ = len(xs)
base = factorial(len_)
assert n < base, "n is too high ({} >= {})".format(n, base)
for i in range(len_ - 1):
base //= len_ - i
offset = n // base
if offset:
# rotate selected value into position
xs[i+1:i+offset+1], xs[i] = xs[i:i+offset], xs[i+offset]
n %= base
return xs
then
>>> permutation(['A', 'B', 'C', 'D'], 15)
['C', 'B', 'D', 'A']
I'm trying to write a function that makes nCk from the list in python
for example from the list for pairs:
['a', 'b', 'c']
output should be:
[['a','b'],['a','c'],['b','c']]
however I'm getting no output
here's my attempt:
def chose(elements, k):
output = []
for i in range(len(elements)):
if k == 1:
output.append(elements[i])
for c in chose(elements[i+1:], k-1):
output.append(elements[i])
output.append(c)
return output
print chose(['a', 'b', 'c'],2)
can you kindly tell what is wrong with function
Use itertools.combinations if you want to find all combinations:
from itertools import combinations
a = ['a', 'b', 'c']
result = [list(i) for i in combinations(a,2)]
The documentation and implementation of the combinations() function can be found on here ...
Update
This function should do what you want:
def chose(elements, k):
output = []
if k == 1:
return [[i] for i in elements]
else:
for i in range(len(elements)):
head = elements[i]
tails = chose(elements[i+1:], k-1)
output += [[head] + tail for tail in tails]
return output
print chose(['a','b','c'], 2)
You can use a powerset without using any imports:
def power_set(items,k):
n = len(items)
for i in xrange(2**n):
combo = []
for j in xrange(n):
if (i >> j) % 2 == 1:
combo.append(items[j])
if len(combo) == k:
yield combo
print(list(power_set(['a', 'b', 'c'],2)))
[['a', 'b'], ['a', 'c'], ['b', 'c']]
I have a string say a = "awxxxyyw".
It should remove all the consecutive elements from the string and return me final string as "a".
ie in 1st iteration xxx seems to be consecutive, so remove xxx, then string becomes awyyw.
2nd iteration removes yy. string becomes aww.
3rd iteration remove ww. returns a
Here is my code.
Where I'm going wrong?
def func(string,pointer):
print pointer
for i in range(pointer,len(string)):
flag=0
temp = i
print temp,pointer
try:
while(string[i-1] == string[i]):
print string
i+= 1
flag = 1
except : break
if flag == 0 :
continue
else:
string = string[0:temp] + string[i+1:len(string)]
func(string, temp)
return string
string = "awxxxyyw"
print func(string,1)
The problem with your code is that you’re only ever removing one character at a time, and so repeated sequences will be reduced to a single character, but that single character will stick around at the end. For example, your code goes from “xxx” to “xx” to “x”, but since there’s only a single “x” left that “x” is not removed from the string. Adapt your code to remove all of the consecutive repeated characters and your problem will be fixed.
I think this is easiest to do with an iterated regular expression substitution:
def remove_dups(s):
import re
pat = re.compile(r"(.)\1+")
while True:
news = pat.sub("", s)
if s == news:
break
s = news
return s
print remove_dups("awxxxyyw") # "a"
print remove_dups("aaxyyza") # "xza"
You need to modify your code to remove all consecutive repeated letters, rather than one at a time. Retaining your recursion:
def func(string):
for i in range(len(string) - 1):
if string[i] == string[i+1]:
j = i + 1
while j < len(string) and string[j] == string[i]:
j += 1
return func(string[:i] + string[j:])
return string
You can do this using itertools.groupby, it allows you to group adjacent similar items:
from itertools import groupby
def solve(x):
while True:
lis = [list(g) for k, g in groupby(x)]
print lis
#if any item in lis has length != 1 then remove all such items
if any(len(x) != 1 for x in lis):
x = ''.join([''.join(x) for x in lis if len(x)==1])
else:
return ''.join([''.join(x) for x in lis])
s = "awxxxyyw"
print solve(s)
print
s = 'aaacccxxxka'
print solve(s)
Output:
[['a'], ['w'], ['x', 'x', 'x'], ['y', 'y'], ['w']] #remove ['x', 'x', 'x'], ['y', 'y'] here
[['a'], ['w', 'w']] #remove ['w', 'w'] here
[['a']] #nothing to remove, returns this as answer.
a
[['a', 'a', 'a'], ['c', 'c', 'c'], ['x', 'x', 'x'], ['k'], ['a']]
[['k'], ['a']]
ka
A working demo:
def func(s):
stack = ['']
idx = 0
while idx < len(s):
if s[idx] == stack[-1]:
el = s[idx]
stack.pop()
while idx < len(s) and s[idx] == el:
idx += 1
else:
stack.append(s[idx])
idx += 1
return ''.join(stack)
print func('awxxxyyw')
'a'
print func('awxyw')
'awxyw'
print func('a')
'a'
print func('abcdefghijklmnoprstuvxywzzwyxvutsrponmlkjihgfedcba')
''
This code is supposed to be able to sort the items in self.array based upon the order of the characters in self.order. The method sort runs properly until the third iteration, unil for some reason the for loop seems to repeat indefinitely. What is going on here?
Edit: I'm making my own sort function because it is a bonus part of a python assignment I have.
class sorting_class:
def __init__(self):
self.array = ['ca', 'bd', 'ac', 'ab'] #An array of strings
self.arrayt = []
self.globali = 0
self.globalii = 0
self.order = ['a', 'b', 'c', 'd'] #Order of characters
self.orderi = 0
self.carry = []
self.leave = []
self.sortedlist = []
def sort(self):
for arrayi in self.arrayt: #This should only loop for the number items in self.arrayt. However, the third time this is run it seems to loop indefinitely.
print ('run', arrayi) #Shows the problem
if self.order[self.orderi] == arrayi[self.globali]:
self.carry.append(arrayi)
else:
if self.globali != 0:
self.leave.append(arrayi)
def srt(self):
self.arrayt = self.array
my.sort() #First this runs the first time.
while len(self.sortedlist) != len(self.array):
if len(self.carry) == 1:
self.sortedlist.append(self.carry)
self.arrayt = self.leave
self.leave = []
self.carry = []
self.globali = 1
self.orderi = 0
my.sort()
elif len(self.carry) == 0:
if len(self.leave) != 0: #Because nothing matches 'aa' during the second iteration, this code runs the third time"
self.arrayt = self.leave
self.globali = 1
self.orderi += 1
my.sort()
else:
self.arrayt = self.array
self.globalii += 1
self.orderi = self.globalii
self.globali = 0
my.sort()
self.orderi = 0
else: #This is what runs the second time.
self.arrayt = self.carry
self.carry = []
self.globali += 1
my.sort()
my = sorting_class()
my.srt()
The key-extractor Alex mentions is trivial enough to put in a lambda function
>>> array = ['ca', 'bd', 'ac', 'ab']
>>> order = ['a', 'b', 'c', 'd']
>>> sorted(array, key=lambda v:map(order.index,v))
['ab', 'ac', 'bd', 'ca']
>>> order = ['b', 'a', 'c', 'd']
>>> sorted(array, key=lambda v:map(order.index,v))
['bd', 'ab', 'ac', 'ca']
>>> order = ['d', 'c', 'b', 'a']
>>> sorted(array, key=lambda v:map(order.index,v))
['ca', 'bd', 'ac', 'ab']
Let's see how this works:
map calls the method order.index for each item in v and uses those return values to create a list.
v will be one of the elements of array
>>> order = ['a', 'b', 'c', 'd']
>>> map(order.index,array[0])
[2, 0]
>>> map(order.index,array[1])
[1, 3]
>>> map(order.index,array[2])
[0, 2]
>>> map(order.index,array[3])
[0, 1]
The function is supplied as a key= to sort, so internally those lists are being sorted instead of the strings.
During the third pass of your loop you are appending new elements to the list you are iterating over therefore you can never leave the loop:
self.arrayt = self.leave - this assignment leads to the fact that self.leave.append(arrayi) will append elements to the list self.arrayt refers to.
In general you may think about creating copies of lists not just assigning different variables/members to the same list instances.
You have self.arrayt = self.leave which makes arrayt refer to exactly the same array as leave (it's not a copy of the contents!!!), then in the loop for arrayi in self.arrayt: you perpetrate a self.leave.append(arrayi) -- which lenghtens self.leave, which is just another name for the very list self.arrayt you're looping on. Appending to the list you're looping on is a good recipe for infinite loops.
This is just one symptom of this code's inextricable messiness. I recommend you do your sorting with the built-in sort method and put your energy into defining the right key= key-extractor function to get things sorted the exact way you want -- a much more productive use of your time.