1

I'm trying to remove all instances of a certain string from a list which contains sublists. For example, something like this:

myarray = ['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]

ends up like this

mylist = [['b'],['c','d'],['d']]

after removing all the instances of 'a'.

I have used this code:

def delnodata(lst, what):
for index, item in enumerate(lst):
    if type(item) == list:
        delnodata(item, what)
    else:
        if item == what:
            lst.remove(item)
delnodata(mylist, 'a')

but the output is:

[['b', 'a'], ['c', 'd'], 'a', ['a', 'd']]

I've seen a lot of similar questions on this site, but unfortunately my programming skills aren't good enough to put this together myself!

timgeb
  • 76,762
  • 20
  • 123
  • 145
mng
  • 65
  • 1
  • 4

4 Answers4

4

I'd do this recursively. This will also work for an arbitrary nesting level.

myarray = ['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]

def nestremove(lst, what):
    new = []
    for item in lst:
        if isinstance(item,list):
            new.append(nestremove(item,what))
        elif item != what:
            new.append(item)
    return new

print(myarray)
myarray = nestremove(myarray, 'a')
print(myarray)

The function returns a new list, so we don't have to remove items from the original list while iterating over it, which as others have already pointed out can be dangerous (see this question, especially the comments). Instead, you can just reassign myarray.

output:

['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]
[['b'], ['c', 'd'], ['d']]
Community
  • 1
  • 1
timgeb
  • 76,762
  • 20
  • 123
  • 145
3

First use for for index, item in enumerate(lst[:]) so it loops through the entire copy of lst second delnodata(myarray, 'a') not delnodata(mylist, 'a') as you have put it

myarray = ['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]
def delnodata(lst, what):
    for index, item in enumerate(lst[:]):
        if type(item) == list: # This if statement is optional
            delnodata(item, what)
        else:
            if item == what:
                lst.remove(item)
    print lst

delnodata(myarray, 'a')
Nobi
  • 1,113
  • 4
  • 23
  • 41
2

The following code returns a new list, with 'a's removed. It doesn't modify lists in place, which can cause mysterious problems.

source

myarray = ['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]

def testme(data):
    for item in data:
        if type(item) is list:
            yield list( testme(item) )
        elif item != 'a':
            yield item

res = list( testme(myarray) )
print res
assert res==[['b'],['c','d'],['d']], res

output

[['b'], ['c', 'd'], ['d']]
johntellsall
  • 14,394
  • 4
  • 46
  • 40
2

removing one item at a time from a list is very inefficient, as the following items all need to be shifted to fill the space each time. You should just create a new filtered list instead

>>> def remover(data, r):
...     return [remover(x, r) if isinstance(x, list) else x for x in data if x != r]
... 
>>> myarray = ['a', 'a', ['b', 'a', 'a'], ['a', 'c', 'd', 'a'], 'a', ['a', 'd']]
>>> remover(myarray, 'a')
[['b'], ['c', 'd'], ['d']]
John La Rooy
  • 295,403
  • 53
  • 369
  • 502