Jhass
Jhass

Reputation: 13

remove zeros between values of a list

I have a list of list and I want to remove zero values that are between numbers in each list. All my lists inside my list have same lenght.

For example:

List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]

desired output:

list2=[[0,1,2,3,0,0],[0,5,6,9,0]]

I was thinking about using indices to identify the first non zero value and last non zero value, but then I don't know how I can remove zeros between them.

Upvotes: 1

Views: 425

Answers (8)

shrey patel
shrey patel

Reputation: 43

You start by replacing 0 by "0" - which is not necessary. Secondly your filter call does not save the resulting list; try:

list1[i] = list(filter(lambda a: a !=0, list1[1:-1])) # changed indexing , I suppose this could work

Upvotes: -1

Green Cloak Guy
Green Cloak Guy

Reputation: 24691

You have the right idea, I think, with finding the first and last indices of nonzeroes and removing zeroes between them. Here's a function that does that:

def remove_enclosed_zeroes(lst):
    try:
        first_nonzero = next(
            i 
            for (i, e) in enumerate(lst) 
            if e != 0
        )
        last_nonzero = next(
            len(lst) - i - 1 
            for (i, e) in enumerate(reversed(lst)) 
            if e != 0
        )
    except StopIteration:
        return lst[:]
    return lst[:first_nonzero] + \
           [e for e in lst[first_nonzero:last_nonzero] if e != 0] + \
           lst[last_nonzero:]

list1 = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
list2 = [remove_enclosed_zeroes(sublist) for sublist in list1]
# [[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]

Upvotes: 3

Nick
Nick

Reputation: 147176

Inspired by @python_user I thought about this a bit more and came up with this simpler solution:

def remove_internal_zeros(lst):
    return [v for i, v in enumerate(lst) if v or not any(lst[i+1:]) or not any(lst[:i])]

This works by passing any value from the original list which is either

  1. not zero (v); or
  2. zero and not preceded by a non-zero value (not any(lst[:i])); or
  3. zero and not followed by a non-zero value (not any(lst[i+1:]))

It can also be written as a list comprehension:

list2 = [[v for i, v in enumerate(lst) if v or not any(lst[:i]) or not any(lst[i+1:])] for lst in list1]

Original Answer

Here's another brute force approach, this pops all the zeros off either end of the list into start and end lists, then filters the balance of the list for non-zero values:

def remove_internal_zeros(l):
    start_zeros = []
    # get starting zeros
    v = l.pop(0)
    while v == 0 and len(l) > 0:
        start_zeros.append(0)
        v = l.pop(0)
    if len(l) == 0:
        return start_zeros + [v]
    l = [v] + l
    # get ending zeros
    end_zeros = []
    v = l.pop()
    while v == 0 and len(l) > 0:
        end_zeros.append(0)
        v = l.pop()
    # filter balance of list
    if len(l) == 0:
        return start_zeros + [v] + end_zeros
    return start_zeros + list(filter(bool, l)) + [v] + end_zeros

print(remove_internal_zeros([0,1,0,2,3,0,0]))
print(remove_internal_zeros([0,5,6,0,0,9,0]))
print(remove_internal_zeros([0,0]))
print(remove_internal_zeros([0,5,0]))

Output:

[0, 1, 2, 3, 0, 0]
[0, 5, 6, 9, 0]
[0, 0]
[0, 5, 0]

Upvotes: 1

Camilo Martínez M.
Camilo Martínez M.

Reputation: 1565

I wrote the following code out of curiosity, that is somewhat intuitive and mimics a "looking item-by-item" approach.

def remove_zeros_inbetween(list_):
    new_list = list_.copy()
    for j, l in enumerate(list_): # loop through the inner lists
        checking = False
        start = end = None
        i = 0
        deleted = 0
        while i < len(l): # loop through the values of an inner list
            if l[i] == 0: # ignore
                i += 1
                continue
            
            if l[i] != 0 and not checking: # non-zero value found
                checking = True # start checking for zeros
                start = i
            elif l[i] != 0 and checking: # if got here and checking, the finish checking
                checking = False
                end = i
                                        
            if start and end: # if both values have been set, i.e, different to None
                # delete values in-between
                new_list[j] = new_list[j][:(start+1-deleted)] + new_list[j][(end-deleted):] 
                deleted += end - start - 1
                
                if l[i] != 0: # for the case of two non-zero values
                    start = i
                    checking = True
                else:
                    i = end # ignore everything up to end
    
                end = None # restart check
            i += 1
            
    return new_list
        
>>> remove_zeros_inbetween([[0, 1, 0, 2, 3, 0, 5], [0, 5, 6, 0, 0, 9, 4]])
    [[0, 1, 2, 3, 5], [0, 5, 6, 9, 4]]

>>> remove_zeros_inbetween([[0, 0], [0, 3, 0], [0]]))
    [[0, 0], [0, 3, 0], [0]]

>>> remove_zeros_inbetween([[0, 0, 0, 0]]))
    [[0, 0, 0, 0]]

Upvotes: 0

Jamie Deith
Jamie Deith

Reputation: 714

One way to do it is to treat each sublist as 3 sections:

  • Zeros at the front, if any
  • Zeros at the end, if any
  • Numbers in the middle from which zeros are to be purged

itertools.takewhile is handy for the front and end bits.

from itertools import takewhile
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]

def purge_middle_zeros(numbers):
    is_zero = lambda x: x==0
    leading_zeros = list(takewhile(is_zero, numbers))
    n_lead = len(leading_zeros)
    trailing_zeros = list(takewhile(is_zero, reversed(numbers[n_lead:])))
    n_trail = len(trailing_zeros)
    mid_numbers = numbers[n_lead:-n_trail] if n_trail else numbers[n_lead:]
    mid_non_zeros = [x for x in mid_numbers if x]
    return leading_zeros + mid_non_zeros + trailing_zeros
    
list2 = [purge_middle_zeros(sub_list) for sub_list in List1]
list2
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]

Other notes:

  • the lambda function is_zero tells takewhile what the criteria are for continuing, in this case "keep taking while it's a zero"
  • for the mid_non_zeros section the list comprehension [x for.... ] takes all the numbers except for the zeros (the if x at the end applies the filter)
  • slicing notation to pick out the middle of the list, numbers[from_start:-from_end] with the negative -from_end meaning 'except for this many elements at the end'. The case where there are no trailing zeros requires a different slice expression, i.e. numbers[from_start:]

Upvotes: 0

Akshay Gupta
Akshay Gupta

Reputation: 1

I wrote a pretty straight-forward approach. Try this.

def removeInnerZeroes(list):
    listHold=[]
    listNew = []
    firstNonZeroFound = False
    for item in list:
        if item==0:
            if firstNonZeroFound:
                listHold.append(item)
            else:
                listNew.append(item)
        else:
            firstNonZeroFound=True
            listHold.clear()
            listNew.append(item)
    listNew.extend(listHold)
    return listNew


complexList = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
print(complexList)
complexListNew = []
for listi in complexList:
    complexListNew.append(removeInnerZeroes(listi))
print(complexListNew)

Upvotes: 0

timhu
timhu

Reputation: 98

Please try this, remove all 0 between numbers in each list.:

list1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]

rowIndex=len(list1)  # count of rows
colIndex=len(list1[0])   # count of columns

for i in range(0, rowIndex):
    noZeroFirstIndex = 1
    noZeroLastIndex = colIndex - 2
    for j in range(1, colIndex - 1):
        if(list1[i][j] != 0):
            noZeroFirstIndex = j
            break
            
    for j in range(colIndex -2, 0, -1):
        if(list1[i][j] != 0):
            noZeroLastIndex = j
            break
    
    for j in range(noZeroLastIndex, noZeroFirstIndex, -1):
        if(list1[i][j] == 0 ):
            del list1[i][j]
        
print(list1)

Result:

[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]

Upvotes: 0

Tim Roberts
Tim Roberts

Reputation: 54718

I think this has to be done with brute force.

new = []
for sub in List1:
    # Find last non-zero.
    for j in range(len(sub)):
        if sub[-1-j]:
            lastnonzero = len(sub)-j
            break
    print(j)
    newsub = []
    firstnonzero = False
    for i,j in enumerate(sub):
        if j:
            firstnonzero = True
            newsub.append(j)
        elif i >= lastnonzero or not firstnonzero:
            newsub.append(j)
    new.append(newsub)

print(new)

Upvotes: 0

Related Questions