Home > Software design >  Loop while list not empty
Loop while list not empty

Time:10-04

I need to loop over a list until list is not empty according to a condition, also This is the code I wrote but is returning a 'list index out of range' error. What is the problem? Any suggestions on how to eventually improve this?

l = [0, 1, 0, 0, 1, 1]

removed= []
while l:
    for i in range(len(l)):
        if l[i]>0:
            l.remove(l[i])
            # store the index processing order
            removed.append(i)
        else:
            continue
    l = [x 1 for x in l]

CodePudding user response:

IIUC - it looks like you just want the index of the removed values and keep the values in the original list if they are less than or equal to and then 1 to the value

l = [0, 1, 0, 0, 1, 1]. # your list

keep_idx, lst = zip(*[(idx, i 1) for idx, i in enumerate(l) if i<=0])

print(list(keep_idx))  # -> [0, 2, 3]
print(list(lst)). # -> [1, 1, 1]

CodePudding user response:

Since you're removing items from list while trying to loop through it, your list length keeps going down but the range doesn't account for it. What you could do is looping through the list backwards. One solution is using

range(len(list_name)-1, -1, -1)

This is to loop through the array backwards and stop the out of range error

CodePudding user response:

@SomeoneElse is right. However, in principle it is recommended to use for this a for loop:

for i in l:
    if i > 0:
        ...

CodePudding user response:

EDIT: I was wrong here, but I will keep the answer up so people can see where I went wrong. As Ignatius pointed out, range() is exclusive of the final item.

Original answer:

Lists use Zero-based indexing, so the first item is item 0.

An example list:

    l = [1, 2, 3, 4, 5]

l contains 5 items, but indexes start at 0, so '1' is l[0] and '5' is l[4].

This is why you're getting an out of range exception. In your code you are looping up to len(l) which is 6, but l[6] is out of bounds. l[5] is the final item in your list.

Solution:

    for i in range(len(l)-1)):

CodePudding user response:

  1. The problem is the list l gets smaller after calling l.remove(value), but subscript 'i' still try to index the original l.

  2. Based on the above analysis, one solution is to keep l unchanged in the inner loop, the other is to keep the unseen i reduced along with l.

# Create new lists to keep `l` unchanged in the inner loop
def method1():
    l = [0, 1, 0, 0, 1, 1]
    removed= []
    while l:
      next_l = []
      [next_l.append(v) if v <= 0 else removed.append(i) for i, v in enumerate(l)]
      l = [x 1 for x in next_l]
    return removed

def method2():
    l = [0, 1, 0, 0, 1, 1]
    removed= []
    while l:
        num_del = 0  # record number of deletions in the inner loop
        for i in range(len(l)):
            if l[i-num_del]>0:
                l.remove(l[i-num_del])
                num_del  = 1
                # store the index processing order
                removed.append(i)
            else:
                continue
        l = [x 1 for x in l]
    return removed


assert method1() == method2()
# output [1, 4, 5, 0, 1, 2]

But I guess you expect the result [1, 4, 5, 0, 2, 3], i.e., record the processing order with subscript in the original list. If so, try this:

l = [0, 1, 0, 0, 1, 1]
el = list(enumerate(l))
removed = []
bound = 0
while len(removed) != len(l):
    removed.extend(list(filter(lambda iv: iv[1] > bound, el)))
    el = list(filter(lambda iv: iv[1] <= bound, el))
    bound -= 1
removed, _ = zip(*removed)
  • Related