Related
I want to generate different combinations of 3 elements a, b, and c. The length of these combinations needs to be 4. I want to have a maximum of 4 times from 'a' and a maximum 1 time from each 'b' and 'c' element. So, for example, we can have ['a',' a',' a','a'] or ['a','a','b','c'] but not ['a','b','b','b'].
There is a similar question in 1, but, as far as I know, using the last 'gen' function, the length of a generation is controlled by the multiplication of a maximum number of repetitions (4 in my case). Also, cases were limited to tuples with exactly 1 'b' and 1 'c' and the rest are 'a'. For the last issue, I replaced 'combinations' with 'combinations_with_replacement', but it still produces tuples with 4 elements and there is no ['a',' a',' a','a'].
How can I tackle this problem?
Here is the code:
from itertools import combinations_with_replacement
def gen(ns, elems=None, C=None, out=None):
if elems is None:
elems = list(range(len(ns)))
else:
assert len(elems) == len(ns)
if out is None:
N = 1
for n in ns:
N *= n
out = [elems[0]]*N
C = range(N)
if len(ns) == 1:
yield out
else:
n = ns[-1]
e = elems[-1]
for c in combinations_with_replacement(C,n):
out_ = out.copy()
for i in c:
out_[i] = e
C_ = [i for i in C if i not in c]
yield from gen(ns[:-1], elems[:-1], C_, out_)
for tmp_list in gen([4,1,1], elems=['a', 'b', 'c'], C=None, out=None):
print(tmp_list)
output:
['c', 'b', 'a', 'a']
['c', 'a', 'b', 'a']
['c', 'a', 'a', 'b']
['b', 'c', 'a', 'a']
['a', 'c', 'b', 'a']
['a', 'c', 'a', 'b']
['b', 'a', 'c', 'a']
['a', 'b', 'c', 'a']
['a', 'a', 'c', 'b']
['b', 'a', 'a', 'c']
['a', 'b', 'a', 'c']
['a', 'a', 'b', 'c']
Please note that since I care about the execution time, I want to generate the tuples in a loop.
I'm creating a text box class with a method that updates its text. It has the user's keyboard input as parameter, keys. The text box class also has an attribute, text, which is pretty self explanatory, it's its text. So I need to add the users input to the text box's text. Except if the user types a backspace key (which is '\x08') I'll need to delete all the letters before it. But of course if there's 2 backspaces followed up consecutively, it's not like a backspace would delete a backspace, I'll need to delete twice instead. So now that you understand the situation, here's my approach to the problem:
def update(self, keys):
if len(keys) > 0:
# I have a shift variable because when you modify a variable at the same time as
# iterating through it, the index will off shift, so I need to reverse that
shift = 0
for i in range(len(keys)):
i -= shift
# If there's a backspace
if keys[i] == '\x08':
# If it's the first character
if i == 0:
# Remove the last character from the text box's text
self.text = self.text[:-1]
# Get rid of the backspace from the user input
keys = keys[1:]
# reverse the off shift, or else the for loop will skip the second element
shift += 1
else:
j = i
# Counts how many backspaces follow up consecutively
del_count = 1
while keys[j] != '\x08':
j -= 1
del_count += 1
# Adjusts the user's input
keys = keys[:j - del_count] + keys[i+1:]
shift += del_count
self.text += keys
print(self.text)
It almost works perfectly, except when the user's input is a character followed up by multiple backspaces, I get an index error. For example, if the user input (keys) was "a\x08\x08", I'd get an index error at if keys[i] == '\x08':. What is the cause for this? And can someone give me a solution? Thanks in advance.
You should not modify the iterable inside its loop.
I was approached this problem within 3 steps:
Merged text and keys first
Count and collect index of backspace chars and effected normal chars
Deleted both of them in the merged object in reversed order
Try this out:
def update(text, keys):
if keys:
# Step 1: Merge both `key` and `texts`
print(repr(text))
text += keys
print(repr(text))
# Step 2: Count and collect `backspace` posision and `effected chars` position in reversed order
backspace_posision = []
char_deleted_position = []
for i, char in enumerate(text[::-1]):
_r_i = - i - 1
if char == "\x08":
backspace_posision.append(_r_i)
print("backspace_posision:", backspace_posision)
elif len(char_deleted_position) < len(backspace_posision):
char_deleted_position.append(_r_i)
print(char)
print("char_deleted_position:", char_deleted_position)
# Step 3: Remove them in reversed order
texts = list(text)
text_leng = len(texts)
print(repr(texts))
for _r_i in sorted(backspace_posision + char_deleted_position, reverse=True):
i = text_leng + _r_i
print(i)
texts.pop(i)
print(repr(texts))
text = "".join(texts)
print(repr(text))
Example output will look like this
update("ABC", "D\x08\x08EFG\x08H\x08\x08")
# Original text
>>>'ABC'
# Merged
>>>'ABCD\x08\x08EFG\x08H\x08\x08'
# Collecting backspace and effected chars in reversed order
# Collecting backspace
>>>backspace_posision: [-1]
>>>backspace_posision: [-1, -2]
# Collecting effected chars
>>>H
>>>char_deleted_position: [-3]
# Collecting backspace
>>>backspace_posision: [-1, -2, -4]
# Collecting effected chars
>>>G
>>>char_deleted_position: [-3, -5]
>>>F
>>>char_deleted_position: [-3, -5, -6]
# Collecting backspace
>>>backspace_posision: [-1, -2, -4, -8]
>>>backspace_posision: [-1, -2, -4, -8, -9]
# Collecting effected chars
>>>D
>>>char_deleted_position: [-3, -5, -6, -10]
>>>C
>>>char_deleted_position: [-3, -5, -6, -10, -11]
# Iterate the merged text in reversed order and remove them
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F', 'G', '\x08', 'H', '\x08', '\x08']
>>>12
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F', 'G', '\x08', 'H', '\x08']
>>>11
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F', 'G', '\x08', 'H']
>>>10
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F', 'G', '\x08']
>>>9
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F', 'G']
>>>8
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E', 'F']
>>>7
>>>['A', 'B', 'C', 'D', '\x08', '\x08', 'E']
>>>5
>>>['A', 'B', 'C', 'D', '\x08', 'E']
>>>4
>>>['A', 'B', 'C', 'D', 'E']
>>>3
>>>['A', 'B', 'C', 'E']
>>>2
>>>['A', 'B', 'E']
>>>'ABE'
So this is the chromatic_scale = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
I want to form major, minor and other scales based on this.
To form a major scale, for example, I need to leap through the chromatic scale like this: major = [2, 2, 1, 2, 2, 2, 1] (in music, we say "tone, tone, semitone, tone, tone, tone, semitone" where tone means leaping 2 list items and semitone means leaping 1, thus the list)
A practical example: Starting from 'C', I should get ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C'] (yes, it should loop back to 'C' at the end).
1 - I thought of doing it with FOR but how do I get FOR to work on a list (chromatic) but leaping through it according to another list (major)?
2 - And if I start from 'A', for instance, how to get it back to the beginning to keep counting?
At the end, I managed to do it but I used WHILE instead of FOR.
chromatic = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B',
'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
def major(tom):
major_scale = [2, 2, 1, 2, 2, 2, 1]
step = 0
t = chromatic.index(tom)
m = []
while len(m) < 8:
m.append(chromatic[t])
if len(m) == 8:
break
t += major_scale[step]
step += 1
return m
x = major('D')
print(x)
Thank you everyone!
Given a list of strings I want to remove the duplicates and original word.
For example:
lst = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
The output should have the duplicates removed,
so something like this ['a', 'b', 'd']
I do not need to preserve the order.
Use a collections.Counter() object, then keep only those values with a count of 1:
from collections import counter
[k for k, v in Counter(lst).items() if v == 1]
This is a O(N) algorithm; you just need to loop through the list of N items once, then a second loop over fewer items (< N) to extract those values that appear just once.
If order is important and you are using Python < 3.6, separate the steps:
counts = Counter(lst)
[k for k in lst if counts[k] == 1]
Demo:
>>> from collections import Counter
>>> lst = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
>>> [k for k, v in Counter(lst).items() if v == 1]
['a', 'b', 'd']
>>> counts = Counter(lst)
>>> [k for k in lst if counts[k] == 1]
['a', 'b', 'd']
That the order is the same for both approaches is a coincidence; for Python versions before Python 3.6, other inputs may result in a different order.
In Python 3.6 the implementation for dictionaries changed and input order is now retained.
t = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
print [a for a in t if t.count(a) == 1]
lst = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
from collections import Counter
c = Counter(lst)
print([k for k,v in c.items() if v == 1 ])
collections.Counter will count the occurrences of each element, we keep the elements whose count/value is == 1 with if v == 1
#Padraic:
If your list is:
lst = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
then
list(set(lst))
would return the following:
['a', 'c', 'b', 'e', 'd']
which is not the thing adhankar wants..
Filtering all duplicates completely can be easily done with a list comprehension:
[item for item in lst if lst.count(item) == 1]
The output of this would be:
['a', 'b', 'd']
item stands for every item in the list lst, but it is only appended to the new list if lst.count(item) equals 1, which ensures, that the item only exists once in the original list lst.
Look up List Comprehension for more information: Python list comprehension documentation
You could make a secondary empty list and only append items that aren't already in it.
oldList = ['a', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
newList = []
for item in oldList:
if item not in newList:
newList.append(item)
print newList
I don't have an interpreter with me, but the logic seems sound.
I have a large number of identical lists 'old' which I want to transform in the same way into a list 'new'. The way I want to do it, is to make an example of the desired list 'new'. Then I turn the difference between the two lists 'old' and 'new' into a rule, and then use that rule to turn my other lists 'old_2' into 'new_2'.
I cannot figure out how to do the first step and the second step does not give me the expected result. Is there an elegant way to do this?
import numpy
# 0 1 2 3 4 5
old_1 = ['A', 'B', 'C', 'D', 'E', 'F']
new = ['B', 'C', 'D', 'E', 'A']
# 01 Get the difference new - /- old_1 based on index positions of
# the list elements, to get something like this:
order = [1,2,3,4,0]
# 02 Then use this order to transform a second identical list, old_2.
# For this I wanted to use the following:
old_2 = ['A', 'B', 'C', 'D', 'E', 'F']
old_2 = numpy.array(old_2)
order = numpy.array(order)
inds = order.argsort()
print('inds =', inds) # As a check, this gives the wrong order: [4 1 0 2 3]
new_2 = old_2[inds]
# I expected this to result in what I want, which is:
print(new_2)
['C', 'B', 'D', 'E', 'A']
# But what I get in reality is this:
inds = [4 1 0 2 3]
['E' 'B' 'A' 'C' 'D']
Any suggestions to get the desired result?
new_2 = ['B', 'C', 'D', 'E', 'A']
From what I understand, I tried to edit your code. Hopefully it helps.
import numpy as np
def get_order(new, old):
order = []
for element in new:
order.append(old.index(element))
return order
def main():
old_1 = ['A', 'B', 'C', 'D', 'E', 'F']
new = ['B', 'C', 'D', 'E', 'A']
order = get_order(new, old_1)
print(order)
old_2 = ['A', 'B', 'C', 'D', 'E', 'F']
old_2 = np.array(old_2)
order = np.array(order)
#inds = order.argsort()
#print('inds =', inds) # As a check, this gives the wrong order: [4 1 0 2 3]
new_2 = old_2[order]
print(new_2)
if __name__ == '__main__':
main()
Output
[1, 2, 3, 4, 0]
['B' 'C' 'D' 'E' 'A']