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.
Related
So I have a list of strings. I want to create a new list of string which turns the same string into a new string and name it "A". If there's a different string in the list, name it "B" and so on.
If the string is:
['F4','A3','F4','B5','A3','K2']
Then it should give me a result of:
['A','B','A','C','B','D']
I don't know how to start the code and can only think of something like a dictionary.
dict = {}
result = []
for line in list1:
if line not in dict:
dict.update({line:str(chr(65+len(dict)))})
result.append(dict.get(line))
Then I don't know how to continue. Any help will be appreciated.
You can make an iterator of ascii upper-case strings and pull them off one-at-a-time in a defaultdict constructor. One you have that, it's just a list comprehension. Something like:
import string
from collections import defaultdict
keys = iter(string.ascii_uppercase)
d = defaultdict(lambda: next(keys))
l = ['F4','A3','F4','B5','A3','K2']
[d[k] for k in l]
# ['A', 'B', 'A', 'C', 'B', 'D']
import string
mapping = {}
offset = 0
for item in l:
if item in mapping:
continue
mapping[item] = string.ascii_uppercase[offset]
offset += 1
[mapping.get(item) for item in l]
Output
['A', 'B', 'A', 'C', 'B', 'D']
You can create a simple class to store the running results:
import string
class L:
def __init__(self):
self.l = {}
def __getitem__(self, _v):
if (val:=self.l.get(_v)) is not None:
return val
self.l[_v]= (k:=string.ascii_uppercase[len(self.l)])
return k
l = L()
vals = ['F4','A3','F4','B5','A3','K2']
result = [l[i] for i in vals]
Output:
['A', 'B', 'A', 'C', 'B', 'D']
Trying to implement and form a very simple algorithm. This algorithm takes in a sequence of letters or numbers. It first creates an array (list) out of each character or digit. Then it checks each individual character compared with the following character in the sequence. If the two are equal, it removes the character from the array.
For example the input: 12223344112233 or AAAABBBCCCDDAAABB
And the output should be: 1234123 or ABCDAB
I believe the issue stems from the fact I created a counter and increment each loop. I use this counter for my comparison using the counter as an index marker in the array. Although, each time I remove an item from the array it changes the index while the counter increases.
Here is the code I have:
def sort(i):
iter = list(i)
counter = 0
for item in iter:
if item == iter[counter + 1]:
del iter[counter]
counter = counter + 1
return iter
You're iterating over the same list that you are deleting from. That usually causes behaviour that you would not expect. Make a copy of the list & iterate over that.
However, there is a simpler solution: Use itertools.groupby
import itertools
def sort(i):
return [x for x, _ in itertools.groupby(list(i))]
print(sort('12223344112233'))
Output:
['1', '2', '3', '4', '1', '2', '3']
A few alternatives, all using s = 'AAAABBBCCCDDAAABB' as setup:
>>> import re
>>> re.sub(r'(.)\1+', r'\1', s)
'ABCDAB'
>>> p = None
>>> [c for c in s if p != (p := c)]
['A', 'B', 'C', 'D', 'A', 'B']
>>> [c for c, p in zip(s, [None] + list(s)) if c != p]
['A', 'B', 'C', 'D', 'A', 'B']
>>> [c for i, c in enumerate(s) if not s.endswith(c, None, i)]
['A', 'B', 'C', 'D', 'A', 'B']
The other answers a good. This one iterates over the list in reverse to prevent skipping items, and uses the look ahead type algorithm OP described. Quick note OP this really isn't a sorting algorithm.
def sort(input_str: str) -> str:
as_list = list(input_str)
for idx in range(len(as_list), 0, -1)):
if item == as_list[idx-1]:
del as_list[idx]
return ''.join(as_list)
I am trying to reverse a list's order by finding three bugs in this function. This function is supposed to reverse the first and last elements of a list, the second and second to last elements, and so on. I believe I found two, but am having trouble fixing the line of list[j] = y.
def reverse(list):
"""Reverses elements of a list."""
for i in range(len(list)):
j = len(list) - i
x = list[i]
y = list[j-1]
list[i] = x
list[j] = y
l = ['a', 'b', 'c', 'd', 'e']
reverse(l)
print(l)
Homework I suspect...
But - we all need a break from homework. By looping over the whole list you're reversing it twice.
def reverse(list):
"""Reverses elements of a list."""
for i in range(len(list)/2):
j = i + 1
x = list[i]
y = list[-j]
list[-j] = x
list[i] = y
l = ['a', 'b', 'c', 'd', 'e']
l=reverse(l)
print(l)
resulting in
['e', 'd', 'c', 'b', 'a']
You have a couple problems. Your first problem is that you use list[j] = y instead of list[j-1] = x. You defined y correctly with j-1, but you should be changing list[j-1] to the other one, x. Another problem is that you are going from the beginning of the list all the way to the end. Once you get to more than half way through the list, you are undoing your work. You also don't need to use len(list)-i because you can just use -i. Here is the updated code:
def reverse(seq):
"""Reverses elements of a list."""
for i in range(len(seq)//2):
x = seq[i]
y = seq[-i-1]
seq[i] = y
seq[-i-1] = x
l = ['a', 'b', 'c', 'd', 'e']
reverse(l)
print(l)
Output:
['e', 'd', 'c', 'b', 'a']
You don't even need to define x and y. Instead, do this:
def reverse(seq):
"""Reverses elements of a list."""
for i in range(len(list)//2):
seq[i], seq[-i-1] = seq[-i-1], seq[i]
I also changed your naming. There's probably a better name than seq, but list is unacceptable because it conflicts with the built-in type.
Use this code:
l = ['a', 'b', 'c', 'd', 'e']
l=l[::-1]
print(l)
Why you want to complicate this simple construction? Or if you don't wanna do this on that way, try to use:
l.reverse()
function. Python has a lot of functions ready to use.
This question already has answers here:
How can I use `return` to get back multiple values from a loop? Can I put them in a list?
(2 answers)
Closed 4 months ago.
I want to know how to return values without breaking a loop in Python.
Here is an example:
def myfunction():
list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
print(list)
total = 0
for i in list:
if total < 6:
return i #get first element then it breaks
total += 1
else:
break
myfunction()
return will only get the first answer then leave the loop, I don't want that, I want to return multiple elements till the end of that loop.
How can resolve this, is there any solution?
You can create a generator for that, so you could yield values from your generator (your function would become a generator after using the yield statement).
See the topics below to get a better idea of how to work with it:
Generators
What does the "yield" keyword do?
yield and Generators explain's
An example of using a generator:
def simple_generator(n):
i = 0
while i < n:
yield i
i += 1
my_simple_gen = simple_generator(3) # Create a generator
first_return = next(my_simple_gen) # 0
second_return = next(my_simple_gen) # 1
Also you could create a list before the loop starts and append items to that list, then return that list, so this list could be treated as list of results "returned" inside the loop.
Example of using list to return values:
def get_results_list(n):
results = []
i = 0
while i < n:
results.append(i)
i += 1
return results
first_return, second_return, third_return = get_results_list(3)
NOTE: In the approach with list you have to know how many values your function would return in results list to avoid too many values to unpack error
Using a generator is a probable way:
def myfunction():
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
total = 0
for i in l:
if total < 6:
yield i #yields an item and saves function state
total += 1
else:
break
g = myfunction()
Now you can access all elements returned with yield i by calling next() on it:
>>> val = next(g)
>>> print(v)
a
>>> v = next(g)
>>> print(v)
b
Or, in a for loop by doing:
>>> for returned_val in myfunction():
... print(returned_val)
a
b
c
d
e
f
What you want is most easily expressed with list slicing:
>>> l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
>>> l[:6]
# ['a', 'b', 'c', 'd', 'e', 'f']
Alternatively create another list which you will return at the end of the function.
def myfunction():
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
ret = []
total = 0
for i in l:
if total < 6:
total += 1
ret.append(i)
return ret
myfunction()
# ['a', 'b', 'c', 'd', 'e', 'f']
A yield statement to create a generator is what you want.
What does the "yield" keyword do in Python?
Then use the next method to get each value returned from the loop.
var = my_func().next()
I'm a newbie I want to write a function that outputs the count of sublists that contain a particular element. But my function just outputs the total count of that particular term in all the sublists.
My function:
def count(myList):
tmp = []
d = {}
for item in myList: tmp += item
for key in tmp: d[key] = d.get(key, 0) + 1
return d
My output:
>>res = count_doc_frequencies([['a', 'b', 'a'], ['a', 'b', 'c'], ['a']])
>>res['a']
4
>>res['b']
2
Desired output:
>>res = count_doc_frequencies([['a', 'b', 'a'], ['a', 'b', 'c'], ['a']])
>>res['a']
3
As 'a' is present in 3 sublists..
can anyone help me modify my function to achieve the desired output ??
lst = [['a', 'b', 'a'], ['a', 'b', 'c'], ['a']]
def count(lst):
# declare dictionary that we are going to return
foo = {}
# iterate sublist
for sublist in lst:
# make sublist into unique element list
sublist = list(set(sublist))
for element in sublist:
# if element found in foo dic, increment
if element in foo:
foo[element] += 1
# else, init with 1
else:
foo[element] = 1
return foo
res = count(lst)
print res
You should change this statement
tmp += item
to
tmp += set(item)
This will eliminate the duplication count of elements in your sublists.
Another way to write this will be
def count(myList,ele):
tmp = []
key = 0
for item in myList:
if ele in item:
key += 1
return key