Different ways of Iterating over a list - python

Can somebody tell me what's the difference between this code:
x = [1, 2, 3, 4, 5, 6, 7]
for i in x[:]:
if i == 5:
x.insert(0, i)
And this code:
x = [1, 2, 3, 4, 5, 6, 7]
for i in x:
if i == 5:
x.insert(0, i)
Why doesn't the second one work? I know it is mentioned in the Python tutorial, but I can't quite understand it.

In the first version, you create a copy (by slicing the list from the beginning to the end), in the second one you're iterating over the original list.
If you iterate over a container, its size can't change during iteration, for good reasons (see below). But since you call x.insert, the size of your list changes.
If you execute the second version, it actually doesn't throw an error immediately, but continues indefinitely, filling the list up with more and more 5s:
Once you're at the list index 4 (and i is therefore 5), you're inserting a 5 at the beginning of the list:
[5, 1, 2, 3, 4, 5, 6, 7]
You then continue in the loop, implicitly increasing your index to 5, which is now again 5, because the whole list got shifted one to the right, due to your insertion, so 5 will be inserted again:
[5, 5, 1, 2, 3, 4, 5, 6, 7]
This goes on "forever" (until there's a MemoryError).

Related

How to remove an element from a list x amount of times?

I want to remove a number by an x amount of times from a list. For example, for this list [1, 4, 3, 6, 4, 3, 2, 4, 8, 11] if I wanted to remove the integer 4 by 2 times, the list would look like this: [1, 3, 6, 3, 2, 4, 8, 11]. What code would be efficient in this situation?
The code I have in the moment:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
result = []
remove = int(input('remove: '))
times = int(input('times: '))
for i in int_list:
if i != remove:
result.append(i)
elements = result
print(elements)
Working in-place
The list method remove removes the first occurrence of the argument from the list. So you can simply call it x amount of times on the list:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
remove = 4
times = 2
for _ in range(times):
int_list.remove(remove)
print(int_list)
Will give:
[1, 3, 6, 3, 2, 4, 8, 11]
Handling errors
In case the element is not found, remove will raise an error. If you want to avoid that:
Check the input pre-hand by making sure there are enough elements to remove using the count method:
if int_list.count(remove) <= times:
# rest of code
If you want to just remove all possible elements, add a check before the remove call:
for _ in range(times):
if remove in int_list:
int_list.remove(remove)
Returning a new list
You can of course simply create a copy of the input list (res = int_list.copy()) and then apply the in-place solution from above. Otherwise, to create a completely new list, use times as a counter for how many times to "skip" the wanted number:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
remove = 4
times = 2
res = []
for num in int_list:
if num == remove and times > 0:
times -= 1
continue
res.append(num)
print(res)
In this case there is no error handling to make. We are simply ignoring the required element a given amount of times. If it doesn't exist at all, less, or more times than given - nothing will happen.

Python list loops

in this code I'm trying to delete every repeated element in the list and just make all of the elements unique and not repeated, so when I run this code give me an error:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
repeat = 0
for i in range(len(myList)-1):
for j in range(len(myList)-1):
if myList[i]== myList[j]:
repeat+=1
if repeat>1:
del myList[j]
print("The list with unique elements only:")
print(myList)
the error which apppears is :
Traceback (most recent call last):
File "main.py", line 8, in <module>
if myList[i]== myList[j]:
IndexError: list index out of range
why is that happens and how can I solve it?
It is a really bad idea to modify an array while looping on it as you have no control on the way things are handled.
May I suggest these two solutions to your problem.
The first one is using set.
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
myList = list(set(myList))
print("The list with unique elements only:")
print(myList)
The other solution is using an other array
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
uniques = []
for number in myList:
if number not in uniques:
uniques.append(number)
print("The list with unique elements only:")
print(uniques)
You can convert list to set, it will automatically delete all of repeated elements
a = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
unique_list = list(set(a))
print(a)
Note: We again convert set to list
What is heppening here is that you are deleting some elements in your list, making it shorter.
Since your for loops are running for the lenght of your original list, you will eventuall try to access an index that no longer exists. This will cause you to get "list index out of range"
To see this for your self, you can add a print statement, like so:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
repeat = 0
for i in range(len(myList)-1):
for j in range(len(myList)-1):
print(i,j,len(myList))
if myList[i]== myList[j]:
repeat+=1
if repeat>1:
del myList[j]
Set data type in Python is used to remove duplicity. Whenever any iterator needs to be viewed with only the unique values in it, it can be converted into a set and that will remove all the duplicate values. For example:
lis=[2,2,3,4]
l=set(lis)
print(l)
Output:
{2, 3, 4}
It can be converted back into the list:
lis=[2,2,3,4]
l=set(lis)
print(l)
l=list(l)
print(l)
Output:
{2, 3, 4}
[2, 3, 4]
Similarly:
myList = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
s=set(myList)
l=list(s)
print(l)
Output:
[1, 2, 4, 6, 9]
Frozen sets can also be used for this purpose. Although; elements of the frozen set remain the same after creation i.e, they can't be modified unlike the elements of the set which are mutable(can be modified).
Hope this was helpful!

Understanding Logic of Inorder traversal of BST, Python

I'm studying for coding interviews and working with a lot of different data structures.
I'm relatively new to Tree problems and have been doing problems daily to practice.
It's one thing to have formulas committed to memory, and another to truly understand them. When I understand something it is easy to apply that understanding to more difficult problems.
Recursive solutions are a little harder for me to mentally visualize and while intuitively they make sense, I am trying to get a deep understanding of what's happening on the Stack.
I have a tree and want to do in order traversal. No problem.
data = []
def checkBST(root):
if root:
checkBST(root.left)
data.append(root.data)
checkBST(root.right)
print(data)
I created the data variable to print out what is being stored on each pass through the method.
It printed
[]
[1]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
I am trying to logically look at what is happening and want to know if my Logic is correct.
There are 15 printed results and 7 Nodes. However we get to 15 because there are 8 places checked for Nodes where Node is None. That happens on Nodes 1, 3, 5, 7.
We are checking the left half of the tree before the right.
[]
#nothing stored because we move onto Node 2 as we don't hit the base case.
[1]
#1 stored because Node 1 doesn't have a left value. So we move onto the append call.
[1]
#1 returned because Node 1 doesn't have a right value.
[1, 2]
#2 stored because because we finished checking the left side and moved onto append data.
[1, 2, 3]
#3 is stored because we are calling the in order traversal on the right side of two now.
[1, 2, 3]
#3 is returned again because it doesn't have a root.left
[1, 2, 3]
#3 is returned again because it doesn't have a root.right
[1, 2, 3, 4]
# we hit the append method for 4, now we move onto the in order traversal on the right
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
The right side will be checked just like the left so I didn't write my logic as it'd be redundant.
I just want clarification if I am looking at this problem in the correct format.
Thanks for any help in understanding this!
The comments in the output are not always correct.
The first output ([]) happens when a function call reaches the end. The first call where this happens is when root is node 1 and from there the first recursive call is made. That call will have None as argument, and so it is the first time a call reaches the print statement.
So we have these ongoing calls:
checkBST(4)
checkBST(2) # left child of 4
checkBST(1) # left child of 2
checkBST(None) # left child of 1
print # --> []
When that deepest call finishes, the function with node 1 will append 1 to the data list, and then make the recursive call for the right child, which also is None and [1] is printed.
Here is a visualisation of the process. The columns represent the depth of the recursion, and the rows represent the sequence of events as time progresses (downwards). The last column is reserved for showing what the current value of data is. When it has a yellow background it means it is printed.
Light blue means that code is executed at that recursion depth. Darker blue means the corresponding function call is pending (on the stack), waiting for the nested recursive call to return.
From this image you can also see why the same output is sometimes repeated: it is printed at different recursion levels as the algorithm is backtracking.

How do you append modified data from a for loop? (Python)

Thanks for checking out my question, am using Python 3 by the way, here is the code:)
scale=[1,2,3,4,5,6]
MNP = {'scale degree' : []
}
for index, value in enumerate(scale):
scale.insert(len(scale),scale[0])
del scale[0]
MNP['scale degree'].append(scale[1])
print (MNP['scale degree'])
So, my goal here is append the modified versions of a list I called scale, into a dict I called MNP. The desired output is a list ('scale degree' that takes the original number of a list (such as scale), and moves it the end of the list, at the same time, all the numbers would shift one to the left, this is what is happens if I give the command to print the scale within the function:
for index, value in enumerate(scale):
scale.insert(len(scale),scale[0])
del scale[0]
MNP['scale degree'].append(scale[1])
print (MNP['scale degree'])
[1, 2, 3, 4, 5, 6]
[2, 3, 4, 5, 6, 1]
[3, 4, 5, 6, 1, 2]
[4, 5, 6, 1, 2, 3]
[5, 6, 1, 2, 3, 4]
[6, 1, 2, 3, 4, 5]
Yet when I try to do so I encountered (yes, past tense) a problem with my understanding. See, if I execute the command:
print(scale[1])
Then it actually does print the numbers 2,3,4,5,6,1 . This is why I used the enumerate function, I thought that this may solve the problem. I thought that, maybe the index number determined somehow which list would be printed off, but this is of course is wrong, even though the output is identical, and, it's worth mentioning that am still very much a stargazed newbie. Yet I did some thinking and realized that it's merely printing off the second value of each modified list, the exact same as if I gave it a single command to print off only one version, yet being a disobedient little bugger if I ask it to append the whole blown thing.
Bloody Hell.
So, now, my question is, how do I append the modified version of the scale, and not just a single value?
Appending the scale as a whole does'st work by the way (this was also my first solution), as the output is then:
[[2, 3, 4, 5, 6, 1]]
[[3, 4, 5, 6, 1, 2], [3, 4, 5, 6, 1, 2]]
[[4, 5, 6, 1, 2, 3], [4, 5, 6, 1, 2, 3], [4, 5, 6, 1, 2, 3]]
it keeps going in this fashion until it reaches [1,2,3,4,5,6] * 6
Thanks in advance for any help, also if you may, could you recommend resources so that I can gain a basic understanding of what's playing behind the scenes? There's probably a lot I still don't understand (I mean in relation only to this problem alone, of course, in general am that close to understanding everything there is to know (haha)).
Thanks again
James
P.S how long in general should I wait till I seek help on SE? (For this particular problem, I've been dueling with if for a solid Day 1/2)
P.S.S A thanks to Borodin for pointing that the manipulation of data within a for loop is bad practice. How should this be correctly executed?
Is this the effect you're after?
>>> l = [1,2,3,4]
>>> l.append(list.pop())
[2,3,4,1]
Or if you want it unrolled a little:
>>> l = [1,2,3,4]
>>> e = list.pop()
>>> l.append(e)
[2,3,4,1]
In both cases, we remove the first element from the list and stick it on the end of the list - which I think is what you're after.
Or if you don't want to modify the original list:
>>> l = [1,2,3,4]
>>> l[1:]+[l[0]]
[2,3,4,1]
Here, we're just recombining slices - the original list is unchanged. Notice that I have to put l[0] into a list because that's the way + is implemented for lists - I can't do l[1:] + l[0].
I think this would help. It has rotate(n) which shifts the elements of the list by n places.
import collections
my_list = []
d = collections.deque([1,2,3,4,5])
for i in range(5):
d.rotate(-1)
my_list.append(d)
print my_list
Output:
[deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5])]

loop until all elements have been accessed N times in python

I have a group of buckets, each with a certain number of items in them. I want to make combinations with one item from each bucket. The loop should keep making different combinations until each item has participated in at least some defined number.
I can easily see how to run the loop and stop once a single element has been accessed a certain number of times. However I can't see how to set a minimum cutoff point beyond searching through all the elements in all the buckets to check their access number after every iteration of the loop.
itertools.product is one way (a very systematic one) to make the "combinations" you request (don't confuse with the .combinations function of course) -- or you could make them randomly with random.choose from each bucket; not sure which one is for you since I don't know what your real purpose is.
Anyway, I'd keep track of how many combos each item has been in with a dict (or one dict per bucket, if there can be overlap in items among buckets). Or, you could use a collections.Counter in Python 2.7, if that's your version.
At any rate, one possibility to do what you request is: the moment an item's count reaches N, remove that item from its bucket (or all buckets, if there's overlap and that's the semantics you require) -- except that if this leaves the bucket empty, restore the bucket's contents and mark that bucked as "done" (you don't need to remove items from a done bucket) e.g. by adding the bucket's index to a set.
You're done when all buckets are done (whether it be randomly or systematically).
Need some code to explain this better? Then please specify the overlap semantics (if overlap is possible) and the systematic-or-random requirements you have.
try
visits = defaultdict(int)
# do at each node visiting
visits[n] += 1
if visits[n] >= MAX_VISITS:
break
print 'done'
Use a dictionary with the items as keys. Every time the item is used, update its count. Then check to see whether all the values are at least above the threshold, ie:
counter = dict()
while min(counter.values) < threshold:
# make a combination
# and update the dictionary
In vanilla Python, this seems to do the job:
buckets = [ [1,2,3],[4],[5,6],[7,8,9,0] ]
def combo(b, i = 0, pref = []):
if len(b) > i:
c = b[i]
for v in c:
combo(b, i + 1, pref + [v])
else:
print pref
combo(buckets)
Output:
[1, 4, 5, 7]
[1, 4, 5, 8]
[1, 4, 5, 9]
[1, 4, 5, 0]
[1, 4, 6, 7]
[1, 4, 6, 8]
[1, 4, 6, 9]
[1, 4, 6, 0]
[2, 4, 5, 7]
[2, 4, 5, 8]
[2, 4, 5, 9]
[2, 4, 5, 0]
[2, 4, 6, 7]
[2, 4, 6, 8]
[2, 4, 6, 9]
[2, 4, 6, 0]
[3, 4, 5, 7]
[3, 4, 5, 8]
[3, 4, 5, 9]
[3, 4, 5, 0]
[3, 4, 6, 7]
[3, 4, 6, 8]
[3, 4, 6, 9]
[3, 4, 6, 0]
There is no doubt a more Pythonic way of doing it.

Categories

Resources