0

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]
Ivan Vinogradov
  • 4,269
  • 6
  • 29
  • 39
  • 1
    what is a ? and ident the code well – sahasrara62 May 10 '19 at 20:38
  • 1
    Think logically..It does not make sense to deal with variable list size in a single iteration. I can recommend using other libraries like pandas/numpy but the easiest one is to create a new list. – mad_ May 10 '19 at 20:38
  • 4
    Possible duplicate of [How to remove items from a list while iterating?](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating) – Valentino May 10 '19 at 21:15

4 Answers4

1

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]
sahasrara62
  • 10,069
  • 3
  • 29
  • 44
  • As the OP has a problem with the processing complexity of creating a new list each time rather than the memory efficiency, this solution still has the same number of append operations as assigning to a new list so it likely doesn't meet the criteria – G. Anderson May 10 '19 at 21:04
1

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.

Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

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)
Ivan Vinogradov
  • 4,269
  • 6
  • 29
  • 39
  • 1
    I have added the comment which makes more sense (at least read the question). Also generator expression might NOT be a good idea as I assume OP needs to iterate over the items multiple times. – mad_ May 10 '19 at 20:54
  • Your comment makes no sense as long as you proposen no solution. – Ivan Vinogradov May 10 '19 at 21:21
  • 2
    There is nothing wrong with pointing out where a proposed answer may not work, according to the constraints set by the original question. Whether or not the person commenting has a solution of their own to offer is not relevant – G. Anderson May 10 '19 at 22:00
0

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.

a small orange
  • 560
  • 2
  • 16