Delete an item from a list - python

Hey, I was trying to delete an item form a list (without using set):
list1 = []
for i in range(2,101):
for j in range(2,101):
list1.append(i ** j)
list1.sort()
for k in range(1,len(list1) - 1):
if (list1[k] == list1[k - 1]):
list1.remove(list1[k])
print "length = " + str(len(list1))
The set function works fine, but i want to apply this method. Except I get:
IndexError: list index out of range
on the statement:
if (list1[k] == list1[k - 1]):
Edited to add
(Thanks to Ned Batchelder) the working code is:
list1 = []
for i in range(2,101):
for j in range(2,101):
list1.append(i ** j)
list1.sort()
k = 0
while k < len(list1) - 1: # while loop instead of for loop because "The range function is evaluated once before the loop is entered"
k += 1
if (list1[k] == list1[k - 1]):
list1.remove(list1[k])
list1.sort()
k -= 1 # "If you find a duplicate, you don't want to move onto the next iteration, since you'll miss potential runs of more than two duplicates"
print "length = " + str(len(list1))

Your code doesn't work because in your loop, you are iterating over all the indexes in the original list, but shortening the list as you go. At the end of the iteration, you will be accessing indexes that no longer exist:
for k in range(1,len(list1) - 1):
if (list1[k] == list1[k - 1]):
list1.remove(list1[k])
The range function is evaluated once before the loop is entered, creating a list of all the indexes in the list. Each call to remove shortens the list by one, so if you remove any elements, you're guaranteed to get your error at the end of the list.
If you want to use a loop like this, try:
k = 1
while k < len(list1):
if list1[k] == list1[k-1]:
del list1[k]
else:
k += 1
I fixed a few other things:
You don't need parentheses around the condition in Python if statements.
If you find a duplicate, you don't want to move onto the next iteration, since you'll miss potential runs of more than two duplicates.
You want to start from index 1, not zero, since k=0 will access list1[-1].

It looks as if you're trying to uniquify a list (clarification would be awesome) so take a look here: http://www.peterbe.com/plog/uniqifiers-benchmark
There is also this question here on SO: In Python, what is the fastest algorithm for removing duplicates from a list so that all elements are unique *while preserving order*?

Instead of removing items Write a list comprehension of the things you want in the new list:
list1[:] = [list1[k] for k in range(1,len(list1) - 1)
if not list1[k] == list1[k - 1] ]
Your method breaks because you remove items from the list. When you do that, the list becomes shorter and the next loop iteration has skipped a item. Say you look at k=0 and L = [1,2,3]. You delete the first item, so L = [2,3] and the next k=1. So you look at L[1] which is 3 -- you skipped the 2!
So: Never change the list you iterate on

You can use del :
l = [1, 2, 3, 4]
del l[2]
print l
[1, 2, 4]

Related

Recursion and List Mutation

I am restrict myself to mutate a list using recursion only, I have a few small troubles when doing so.
def expand_strings(L,s,i):
if len(L) == 0:
return
else:
if len(L[0]) - 1 <= i:
L[0] += s
elif len(L[0]) - 1 > i:
new_string = L[0][:i] + s + L[0][i:]
L[0] = new_string
expand_strings(L[1:], s, i)
return
L: The input list containing possible 1 or more strings
s: The extra portion of string that I need to "insert" or "append" to the string elements within the list
i: The index of string where I want to insert or append s to.
The main goal of this function is the following:
1. if the index i within the range 0 ~ len(string_element_in_list), then I insert my s starting from index i
2. if the index i is larger than what the current string length, then I do the append s.
The problems I am having right now is that: I notice the recursion will only mutate the first element within the input list, and every element after the first element, they won't be affected by the mutation, I figure it might have something to do with the new input list I pass to the recursion, but I don't know precisely why this doesn't work.
Thanks for helping in advance. :)
The problem is in the recursive call expand_strings(L[1:], s, i). When you use slicing to get a part of your list, python creates a whole new copy of that sublist. So the recursive call creates a copy of your list, except the first element, and works on that copy.
One way of solving this problem can be returning the modified list from your method:
def expand_strings(L,s,i):
if len(L) == 0:
return []
else:
if len(L[0]) - 1 <= i:
L[0] += s
elif len(L[0]) - 1 > i:
new_string = L[0][:i] + s + L[0][i:]
L[0] = new_string
return [L[0]] + expand_strings(L[1:], s, i)
If you don't want to create a copy of the sublist every time (and return the modified list), you can add one more parameter to your method that would specify the location of the first element to modify. The base case would be where the starting index is equal to the length of the list.
def expand_strings(L,s,i,start):
if start == len(L):
return
if len(L[start]) - 1 <= i:
L[start] += s
else:
L[start] = L[start][:i] + s + L[start][i:]
expand_strings(L, s, i, start + 1)

Deleting element from array dynamically and reducing the size of the array

Need a solution to fixing the list size after deleting an element while iterating it.
Cannot create a new list, as the issue for a bigger problem (800 iterations) and it is computational expensive to make a new list every time
list = [1,2,3,4,5,6,7,8,9,10]
for i in range (len(list)):
if a[i] < 6:
del a[i]
output should be the original list, with values now
[6,7,8,9,10]
just overwrite the current list ,
lst = [1,2,3,4,5,6,7,8,9,10]
lst =[i for i in lst if i>=6]
# lst = [6,7,8,9,10]
See the UPDATE at the end of this answer for performance measurements
If you're going to only delete one element from the list (or elements at the beginning of the list), you may want to find the index instead of going through all of them:
index = lst.index(6)
del lst[:index]
If you're concerned with index offsets while traversing the list, you can keep track of the number of deleted entries and compute the actual index accordingly:
originalLen = len(lst)
for originalIndex in range(originalLen):
i = originalIndex - originalLen + len(lst)
if lst[i] < 6:
del lst[i]
You could even generalize this by creating an iterator that does the housekeeping for you:
def stableIndexes(lst):
oLen = len(lst)
for oi in range(oLen): yield oi - oLen + len(lst)
for i in stableIndexes(lst):
if lst[i] < 6:
del lst[i]
If you're going to delete multiple items, you could create a list of indexes to delete and process them in reverse order at the end of the loop:
indexes = []
for i,a in enumerate(lst):
if a > 2 and a < 5:
indexes.append(i)
for index in reversed(indexes):
del lst[index]
or you can process the list in reverse order and delete as you go without indexes getting mixed up:
for i in range(len(lst)-1,-1,-1):
if lst[i] > 2 and lst[i] < 5:
del lst[i]
Another way to do it would be to manually shift the subsequent items after your delete at least one and truncate the list at the end:
i = 0
for index,a in enumerate(lst):
if a > 2 and a < 5:
continue
if i < index:
lst[i] = a
i += 1
del lst[i:]
Finally, an alternative approach could be to assign None to the items that you want to delete and skip the None values on subsequent iterations:
for i,a in enumerate(lst):
if a is None: continue
if a > 2 and a < 5:
lst[i] = None
...
UDATE
I made a few performance tests deleting entries from a 1,000,000 element list. It turns out that using a list comprehension (i.e. making a second copy of the list) is faster than all of the schemes I described above:
Method del 1 in 13 del only 1
---------------------------------- ----------- ----------
New list (using comprehension): 0.00650 0.00799
Assign None to deleted items: 0.00983 0.01152
Manually shifting elements: 0.01558 0.01741
Delete as you go in reverse order: 0.07436 0.00942
List of indexes to delete: 0.09998 0.01044
So, the "computationally expensive" theory of making a new list doesn't hold true in practice. When compared to all other methods, it is actually the most economical approach in terms of processing time.
This is most likely caused by COW (Copy On Write) memory management which will allocate memory as soon as you change something in the list. Thus there will always be a new list created (internally).
By explicitly creating a new list yourself, you take advantage of this memory allocation and save on any additional shifting of data within the list's memory space.
In conclusion, you should let Python handle these concerns.
First of all, it's easier to use list comprehensions:
lst = [1,2,3,4,5,6,7,8,9,10]
t = [x for x in lst if x >= 6]
print(t)
And if you're not allowed to create a new list object, you can always use a generator expression:
lst = [1,2,3,4,5,6,7,8,9,10]
g = (x for x in lst if x >= 6)
for val in g:
print(val)
A safe (if modifying a list over which one iterates may be called "safe" to any extent) solution is to iterate backwards:
n = len(list_)
for i in xrange(n):
idx = n - i - 1
if list_[idx] < 6:
del list_[idx]
This way, you will at least not have changes impact the unchanged part of the list.

What is the correct loop range

I want to delete some array from list. But I'm using wrong range.
At start the range is correct.
This should work, if string in variable result[b][2:3] then delete result[b]
for b in range(len(result)):
if 'FillLevel' in result[b][2:3]:
del result[b]
After that I have error: IndexError: list index out of range
I want to find this string and delete whole line (array):
V;4;FillLevel[1];CPUA.DB1610.0,I0,64;RW
V;4;FillLevel[2];CPUA.DB1610.0,I;RW
V;4;FillLevel[5];CPUA.DB1610.6,I;RW
V;4;FillLevel[6];CPUA.DB1610.8,I;RW
V;4;FillLevel[11];CPUA.DB1610.18,I;RW
Why this code:
print(result[4][2:3])
print(result[5][2:3])
print(result[6][2:3])
print(result[7][2:3])
print(result[8][2:3])
print(result[9][2:3])
print(result[10][2:3])
b = 0
while b < len(result):
if 'FillLevel' in result[b][2:3]:
del result[b]
del adress[b]
print('yes')
b += 1
Showing only once 'yes' ?
['FillLevel']
['FillLevel[1]']
['FillLevel[2]']
['FillLevel[3]']
['FillLevel[4]']
['FillLevel[5]']
['FillLevel[6]']
yes
The issue is that del result[b] changes the composition (and the length of) result, thereby interfering with your loop.
Perhaps the easiest way to fix this is by rephrasing your code as a list comprehension:
result = [r for r in result if 'FillLevel' not in r[2:3]]
Alternatively, you could fix it by iterating in reverse:
for b in range(len(result) - 1, -1, -1):
if 'FillLevel' in result[b][2:3]:
del result[b]
Let's say there are 10 items in the list.
Half-way through you delete one of the items; now there are 9 items in the list.
In the last cycle, your loop asks for the tenth item. My guess is that's where the index error is happening (though it could be due to the [2:3] call as well, depending on the contents of your list)
A more pythonic solution would be
result = [val for val in result if 'FillLevel' not in val[2:3]]
If you want to preserve the same list and parse it in the strait order you can use a while loop which evaluate the len(result) in each iteration
b = 0
while b < len(result) :
if 'FillLevel' in result[b][2:3]:
del result[b]
b += 1
for first
- it's mach easyer to iterate by list withot getting length, probably you are got an error coz length of list is changing during loop
for second
- you are trying to check 'FillLevel' in slice of string. slice return one character
- try to not midify your list but make new one with filtered items
like this:
new_list = []
for b in result:
if 'FillLevel' not in b:
new_list.append(b)
or check about List Comprehensions and type this:
[i for i in result if 'FillLevel' not in i]

Iterating through a list and comparing places - Python

So, I am trying something simple. I am trying to go through a list, which, once sorted, I can see if the next number along is higher than the previous.
If this is so, I can add it into a new list:
def longestRun(L):
'''
assume L is not empty
'''
new_list=[]
list_place=0
# we then need to iterate along the sorted List L
print L[list_place]
print L[list_place+1]
if L[list_place] < L[list_place+1]
new_list+=L[list_place]
list_place+=1
L = [1, 2, 3]
list_place = 0
new_list = []
while list_place < len(L)-1:
print L[list_place]
if L[list_place] < L[list_place+1]:
new_list.append(L[list_place])
list_place+=1
print len(new_list)
To iterate, first you need a loop statement, like while. Second, if you don't increment list_place every time you iterate, once if statement evaluates to false, it goes into loop forever.

'For' index changing because of list.pop() call

I have this Python code which confronts, one by one, the items in a list of integers (named 'seen' in the code posted) with all the items in the .f field of another list (named 'maxx' in the code posted).
At every iteration I'm counting (through the c variable) how many times does the j-th item appear in the 'maxx' list, and I want to pop() it from the list if it appears less than three times.
The code works perfectly, but popping an item 'pulls' any subsequent item in the 'seen' list back by one position, therefore every time the if condition is satisfied the loop misses the very next item of the list.
Here is the code:
for indj,j in enumerate(seen): # every item in the 'seen' list..
c=0
for k in maxx: # ..checks for a matching item in the 'maxx' list
if j==k.f:
c=c+1;
if c<3: # if the item appears less than 3 times we pop it
seen.pop(indj)
I have tried to add:
indj=indj-1
j=seen[indj]
At the end of the if construct, but it didn't work
You have to make a new list or work with a copy. When you change a list while looping over it you skip some items. I'd do this:
def filter_low(lst, maxk, threshold=3):
for item in lst:
c = sum(1 for k in maxx if item==k.f)
if c >= threshold:
yield item
new_seen = list(filter_low(seen, maxk, 3))
Which is the same as:
new_seen = [item for item in seen
if sum(1 for k in maxx if item==k.f) >= 3]
You can change the original list by doing
seen[:] = [item for item in seen
if sum(1 for k in maxx if item==k.f) >= 3]
Modifying the list you're iterating over is never a good idea. You could iterate over a copy and modify the actual list with
popped = 0
for indj, j in enumerate(seen[:]):
s = sum(j == k.f for k in maxx)
if s < 3:
seen.pop(indj - popped)
popped += 1
If the seen list is very large, this might be inefficient.

Categories