Removing last list element by popping - python

I have the following piece of Python code where I am trying to empty out list A by removing one element at a time starting from the end. I cannot seem to reduce the list to an empty one and I would like to know why not. Any insight would be extremely appreciated.
A = [3,4,5,6,2]
for i in A:
A.pop()

var = [3,4,5,6,2]
for x in range(len(var)):
a = var.pop(-1)
print(a)
or reverse a list
var = var[::-1]

This issue you are facing because you are trying to iterate the loop from first element and trying to remove the last element of the list. at one pint for loop runs out of element in a list hence it stops and you don't get empty list.
The proper solution will be to reverse iterate through the list and remove the elements.
Sample Code :
A = [3,4,5,6,2]
for i in range( len(A) -1 , -1, -1):
A.pop()
print (A)

You can try this:
A = [3,4,5,6,2]
for a in range(len(A)):
A.pop(-1)
Output:
>>> A
[]
You can also use a list comprehension instead of a traditional for loop:
A = [3,4,5,6,2]
[A.pop(-1) for a in range(len(A))]
Output:
>>> A
[]

Here you are trying to iterate through the elements of a list while the length of the list is reduced by 1 for each iteration.
This is not the right way to do this,
Try this,
A = [3,4,5,6,2]
for _ in range(len(A)):
A.pop()
This will work.
Note: Never loop through a list in which you are going to perform some operations inside the loop-body. Try duplicating the list or use some other conditions.

Related

Element was not removed from list in Python 3

d = 3
cl = [1,3,4]
for i in cl:
if i <= d:
cl.remove(i)
print(cl)
output >> [3,4]
The number 3 should not be in the list as it passed the i <= d condition so cl.remove should've been called but it is in the output. What am I doing wrong here?
It happens because you're removing elements from list while iterating over it. It could be solved like this
[num for num in cl if num > d]
This works
d = 3
cl = [1, 3, 4]
for i in range(len(cl)):
if cl[i-1] <= d:
cl.remove(cl[i-1])
print(cl)
Please can you accept if it works...
This is the result of mutating a data structure during iteration. The for loop essentially creates an iterator over your list, with each item calling next(iterator). However, popping items off changes what the iterator is looking at
a = [1, 2, 3, 4]
it = iter(a)
# First iteration
next(it)
1
# remove element
a.pop(0)
1
# we skipped 2!
next(it)
3
Why? Well, we effectively changed what element the iterator is pointing to by removing the element we were currently on. We were looking at the first element in the sequence, but that was removed, so now the second element is the first one. So the call to next then points to the following element. This winds up looking like it was skipped when it wasn't, you just unintentionally had elements shuffled forward.
To avoid this, it's best to create a new list by filtering, as #AlexanderLekontsev suggests in his answer. This avoids mutating while iterating. You can do this with a standard loop with append like so:
newlist = []
for num in cl:
if num >= d:
newlist.append(num)
Essentially, because you are removing elements from the list while iterating over it, you have skipped over the value 3.
In the first iteration of your for-loop, you remove the value 1. Because of how iterations work in Python, in your second iteration of the for-loop, you are looking for next(next(cl)). But, cl has been updated to [3,4], since you removed 1. So now next(next(cl)) = next(3) = 4 and so you've skipped over 3.
You can resolve this by creating a new list and updating it as you go along. An easy way to do this using list comprehension is simply [num for num in cl if num > d].

I want to write a function that takes a list and returns it with all duplicates removed, without creating another list or string

I have tried this, it gives the output it should give([1,3,2]), however the problem is it keeps printing the output for infinite times without stop,, is there any solutions with out changing the idea of the code.
a= [1,2,2,2,1,3,2]
def rem_dup(L):
while len(L):
for i in L:
y= L.count(i)
if y>1:
L.remove(i)
print L
rem_dup(a)
Unless the point of this function is to exercise your python skills, it sounds like you want a set. A set is like a list but does not allow duplicate values. If you want your final data structure to be a list, you could do something like this:
final_list = list(set(original_list))
One way to safely do this is to loop over the list in reverse and remove only from the back:
>>> for i in range(len(a) - 1, -1, -1):
... if a.count(a[i]) > 1:
... del a[i]
...
>>> a
[1, 2, 3]
But this will be polynomial time, since a.count is linear and so is del a[i].
while len(L) will always be true as long as L had something in it to begin with
Modifying L while using it with the for loop can cause items to be skipped, so you have a bug for some inputs.
If you fix that problem, you shouldn't need the while loop.
As long as the items in a are hashable and you don't mind that the remaining items aren't in the same order when you started, you can create an intermediate set and replace the original contents in-place.
a[:] = set(a)

Creating a function that removes duplicates in list

I'm trying to manually make a function that removes duplicates from a list. I know there is a Python function that does something similar (set()), but I want to create my own. This is what I have:
def remove(lst):
for i in range(len(lst)):
aux = lst[0:i] + lst[i+1:len(lst)]
if lst[i] in aux:
del(lst[i])
return lst
I was trying something like creating a sub-list with all the items except the one the for is currently on, and then check if the item is still in the list. If it is, remove it.
The problem is that it gives me an index out of range error. Does the for i in range(len(lst)): line not update every time it starts over? Since I'm removing items from the list, the list will be shorter, so for a list that has 10 items and 2 duplicates, it will go up to index 9 instead of stopping on the 7th.
Is there anyway to fix this, or should I just try doing this is another way?
I know this does not fix your current script, but would something like this work?
def remove(lst):
unique=[]
for i in lst:
if i not in unique: unique.append(i)
return unique
Just simply looping through, creating another list and checking for membership?
The problem is you are manipulating the list as you are iterating over it. This means that when you reach the end of the list, it is now shorter because you're removed elements. You should (generally) avoid removing elements while you are looping over lists.
You got it the first time: len(lst) is evaluated only when you enter the loop. If you want it re-evaluated, try the while version:
i = 0
while i < len(lst):
...
i += 1
Next, you get to worry about another problem: you increment i only when you don't delete an item. When you do delete, shortening the list gets you to the next element.
i = 0
while i < len(lst):
aux = lst[0:i] + lst[i+1:len(lst)]
if lst[i] in aux:
del(lst[i])
else:
i += 1
I think that should solve your problem ... using the logic you intended.
def remove(lst):
new_list = []
for i in lst:
if i not in new_list:
new_list.append(i)
return new_list
You should append the values to a secondary list. As Bobbyrogers said, it's not a good idea to iterate over a list that is changing.
You can also try this:
lst = [1,2,3,3,4,4,5,6]
lst2 = []
for i in lst:
if i not in lst2:
lst2.append(i)
print(lst2)
[1, 2, 3, 4, 5, 6]

python list.iteritems replacement

I've got a list in which some items shall be moved into a separate list (by a comparator function). Those elements are pure dicts. The question is how should I iterate over such list.
When iterating the simplest way, for element in mylist, then I don't know the index of the element. There's no .iteritems() methods for lists, which could be useful here. So I've tried to use for index in range(len(mylist)):, which [1] seems over-complicated as for python and [2] does not satisfy me, since range(len()) is calculated once in the beginning and if I remove an element from the list during iteration, I'll get IndexError: list index out of range.
Finally, my question is - how should I iterate over a python list, to be able to remove elements from the list (using a comparator function and put them in another list)?
You can use enumerate function and make a temporary copy of the list:
for i, value in enumerate(old_list[:]):
# i == index
# value == dictionary
# you can safely remove from old_list because we are iterating over copy
Creating a new list really isn't much of a problem compared to removing items from the old one. Similarly, iterating twice is a very minor performance hit, probably swamped by other factors. Unless you have a very good reason to do otherwise, backed by profiling your code, I'd recommend iterating twice and building two new lists:
from itertools import ifilter, ifilterfalse
l1 = list(ifilter(condition, l))
l2 = list(ifilterfalse(condition, l))
You can slice-assign the contents of one of the new lists into the original if you want:
l[:] = l1
If you're absolutely sure you want a 1-pass solution, and you're absolutely sure you want to modify the original list in place instead of creating a copy, the following avoids quadratic performance hits from popping from the middle of a list:
j = 0
l2 = []
for i in range(len(l)):
if condition(l[i]):
l[j] = l[i]
j += 1
else:
l2.append(l[i])
del l[j:]
We move each element of the list directly to its final position without wasting time shifting elements that don't really need to be shifted. We could use for item in l if we wanted, and it'd probably be a bit faster, but when the algorithm involves modifying the thing we're iterating over, I prefer the explicit index.
I prefer not to touch the original list and do as #Martol1ni, but one way to do it in place and not be affected by the removal of elements would be to iterate backwards:
for i in reversed(range(len()):
# do the filtering...
That will affect only the indices of elements that you have tested/removed already
Try the filter command, and you can override the original list with it too if you don't need it.
def cmp(i): #Comparator function returning a boolean for a given item
...
# mylist is the initial list
mylist = filter(cmp, mylist)
mylist is now a generator of suitable items. You can use list(mylist) if you need to use it more than once.
Haven't tried this yet but.. i'll give it a quick shot:
new_list = [old.pop(i) for i, x in reversed(list(enumerate(old))) if comparator(x)]
You can do this, might be one line too much though.
new_list1 = [x for x in old_list if your_comparator(x)]
new_list2 = [x for x in old_list if x not in new_list1]

Searching for substring in element in a list an deleting the element

I have a list and I am trying to delete the elements that have 'pie' in them. This is what I've done:
['applepie','orangepie', 'turkeycake']
for i in range(len(list)):
if "pie" in list[i]:
del list[i]
I keep getting list index out of range, but when I change the del to a print statement it prints out the elements fine.
Instead of removing an item from the list you're iterating over, try creating a new list with Python's nice list comprehension syntax:
foods = ['applepie','orangepie', 'turkeycake']
pieless_foods = [f for f in foods if 'pie' not in f]
Deleting an element during iteration, changes the size, causing IndexError.
You can rewrite your code as (using List Comprehension)
L = [e for e in L if "pie" not in e]
Something like:
stuff = ['applepie','orangepie', 'turkeycake']
stuff = [item for item in stuff if not item.endswith('pie')]
Modifying an object that you're iterating over should be considered a no-go.
The reason to why you get a error is because you change the length of the list when you delete something!
Example:
first loop: i = 0, length of list will become 1 less because you delete "applepie" (length is now 2)
second loop: i = 1, length of list will now become just 1 because we delete "orangepie"
last/third loop: i = 2, Now you should see the problem, since i = 2 and the length of the list is only 1 (to clarify only list[0] have something in it!).
So rather use something like:
for item in in list:
if "pie" not in item:
new list.append(item)
Another but longer way would be to note down the indexes where you encounter pie and delete those elements after the first for loop

Categories